Skip to content

Commit 23b6ece

Browse files
topepo‘topepo’cderv
authored
function for making new blog posts (#171)
Add new function and related tests Co-authored-by: ‘topepo’ <‘[email protected]’> Co-authored-by: Christophe Dervieux <[email protected]>
1 parent 8a0d1ee commit 23b6ece

File tree

10 files changed

+320
-11
lines changed

10 files changed

+320
-11
lines changed

DESCRIPTION

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: quarto
22
Title: R Interface to 'Quarto' Markdown Publishing System
3-
Version: 1.4.4.9012
3+
Version: 1.4.4.9013
44
Authors@R: c(
55
person("JJ", "Allaire", , "[email protected]", role = "aut",
66
comment = c(ORCID = "0000-0003-0174-9868")),
@@ -19,6 +19,7 @@ Depends:
1919
R (>= 3.6)
2020
Imports:
2121
cli,
22+
fs,
2223
jsonlite,
2324
later,
2425
processx,
@@ -27,6 +28,7 @@ Imports:
2728
rstudioapi,
2829
tools,
2930
utils,
31+
xfun,
3032
yaml
3133
Suggests:
3234
bslib,
@@ -48,7 +50,7 @@ Suggests:
4850
thematic,
4951
tidyverse,
5052
withr,
51-
xfun
53+
whoami
5254
VignetteBuilder:
5355
quarto
5456
Config/testthat/edition: 3

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Generated by roxygen2: do not edit by hand
22

33
export(is_using_quarto)
4+
export(new_blog_post)
45
export(quarto_add_extension)
56
export(quarto_available)
67
export(quarto_binary_sitrep)
@@ -42,4 +43,5 @@ importFrom(rstudioapi,isAvailable)
4243
importFrom(rstudioapi,viewer)
4344
importFrom(tools,vignetteEngine)
4445
importFrom(utils,browseURL)
46+
importFrom(yaml,as.yaml)
4547
importFrom(yaml,write_yaml)

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# quarto (development version)
22

3+
- Added a `new_blog_post()` function (thanks, @topeto, #22).
4+
35
- Make `quarto_render(as_job = TRUE)` wrapable (thanks, @salim-b, #105).
46

57
- Quarto CLI will now correctly use the same R version than the one used to run functions in this package (#204).

R/blog.R

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#' Create a new blog post
2+
#'
3+
#' Creates (and potentially opens) the `index.qmd` file for a new blog post.
4+
#'
5+
#' @inheritParams rlang::args_error_context
6+
#' @param title A character string for the title of the post. It is converted
7+
#' to title case via [tools::toTitleCase()].
8+
#' @param dest A character string (or NULL) for the path within `posts`. By
9+
#' default, the title is adapted as the directory name.
10+
#' @param wd An optional working directory. If `NULL`, the current working is used.
11+
#' @param open A logical: have the default editor open a window to edit the
12+
#' `index.qmd` file?
13+
#' @param ... A named list of values to be added to the yaml header, such as
14+
#' `date`, `author`, `categories`, `description`, etc.
15+
#' If no `date` is provided, the current date is used.
16+
#' If no `author` is provided, `whoami::fullname()` is used to get the user's name.
17+
#' @return The path to the index file.
18+
#' @export
19+
#' @examples
20+
#' \dontrun{
21+
#' \donttest{
22+
#' new_blog_post("making quarto blog posts", categories = c("R"))
23+
#'
24+
#' }
25+
#' }
26+
#'
27+
new_blog_post <- function(
28+
title,
29+
dest = NULL,
30+
wd = NULL,
31+
open = rlang::is_interactive(),
32+
call = rlang::current_env(),
33+
...
34+
) {
35+
rlang::check_installed("whoami")
36+
37+
if (is.null(dest)) {
38+
# Scrub title to make directory name
39+
dest <- gsub("[[:space:]]", "-", tolower(title))
40+
}
41+
dest_path <- make_post_dir(dest, wd, call)
42+
post_yaml <- make_post_yaml(title, ...)
43+
qmd_path <- write_post_yaml(post_yaml, dest_path, call)
44+
if (open) {
45+
edit_file <- utils::file.edit
46+
if (
47+
rlang::is_installed("usethis") &&
48+
is.function(asNamespace("usethis")$edit_file)
49+
) {
50+
edit_file <- utils::getFromNamespace("edit_file", "usethis")
51+
}
52+
edit_file(qmd_path)
53+
}
54+
invisible(qmd_path)
55+
}
56+
57+
make_post_dir <- function(dest, wd, call) {
58+
working <- if (is.null(wd)) fs::path_wd() else fs::path_abs(wd)
59+
60+
# is this a quarto project for blog ? Expecting _quarto.yml in working dir
61+
if (!fs::file_exists(fs::path(working, "_quarto.yml"))) {
62+
cli::cli_abort(
63+
"You need to be at root of a Quarto project to create a blog post in the {.file posts/} directory at {.file {fs::path_real(working)}}.",
64+
call = call
65+
)
66+
}
67+
68+
post_path <- fs::path(working, "posts", dest)
69+
70+
if (fs::dir_exists(post_path)) {
71+
cli::cli_abort(
72+
"There is already a {.file {dest}} directory in 'posts/'",
73+
call = call
74+
)
75+
} else {
76+
ret <- fs::dir_create(post_path)
77+
}
78+
ret
79+
}
80+
81+
make_post_yaml <- function(title, ...) {
82+
default_values <- list(
83+
title = tools::toTitleCase(title),
84+
author = tools::toTitleCase(whoami::fullname("Your name")),
85+
date = format(Sys.Date(), "%Y-%m-%d"),
86+
categories = character(0)
87+
)
88+
89+
user_values <- list(...)
90+
91+
yml_values <- merge_list(default_values, user_values)
92+
if (length(yml_values$categories) == 0) {
93+
yml_values <- yml_values[names(yml_values) != "categories"]
94+
}
95+
yml_values <- as_yaml(yml_values)
96+
yml_values <- paste0("---\n", yml_values, "---\n")
97+
yml_values
98+
}
99+
100+
write_post_yaml <- function(x, dest, call) {
101+
dest_file <- fs::path(dest, "index.qmd")
102+
if (fs::file_exists(dest_file)) {
103+
cli::cli_abort(
104+
"There is already am index.qmd file at {.code {path}}",
105+
call = call
106+
)
107+
} else {
108+
ret <- xfun::write_utf8(x, con = dest_file)
109+
}
110+
dest_file
111+
}

R/utils.R

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,25 @@ relative_to_wd <- function(path) {
33
rmarkdown::relative_to(getwd(), path)
44
}
55

6+
# Specific YAML handlers
7+
# as quarto expects YAML 1.2 and yaml R package supports 1.1
8+
yaml_handlers <- list(
9+
# Handle yes/no from 1.1 to 1.2
10+
# https://github.com/vubiostat/r-yaml/issues/131
11+
logical = function(x) {
12+
value <- ifelse(x, "true", "false")
13+
structure(value, class = "verbatim")
14+
}
15+
)
16+
17+
#' @importFrom yaml as.yaml
18+
as_yaml <- function(x) {
19+
yaml::as.yaml(x, handlers = yaml_handlers)
20+
}
21+
622
#' @importFrom yaml write_yaml
723
write_yaml <- function(x, file) {
8-
handlers <- list(
9-
# Handle yes/no from 1.1 to 1.2
10-
# https://github.com/vubiostat/r-yaml/issues/131
11-
logical = function(x) {
12-
value <- ifelse(x, "true", "false")
13-
structure(value, class = "verbatim")
14-
}
15-
)
16-
yaml::write_yaml(x, file, handlers = handlers)
24+
yaml::write_yaml(x, file, handlers = yaml_handlers)
1725
}
1826

1927

@@ -34,3 +42,13 @@ in_positron <- function() {
3442
in_rstudio <- function() {
3543
identical(Sys.getenv("RSTUDIO"), "1")
3644
}
45+
46+
47+
# for test
48+
49+
hide_path <- function(path) {
50+
function(x) {
51+
x <- gsub(path, "<project directory>", x, fixed = TRUE)
52+
gsub(fs::path_real(path), "<project directory>", x, fixed = TRUE)
53+
}
54+
}

_pkgdown.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ reference:
3434
More about Quarto extensions at <https://quarto.org/docs/projects/quarto-projects.html>
3535
contents:
3636
- quarto_create_project
37+
- new_blog_post
3738

3839
- title: "Configuration"
3940
desc: >

man/new_blog_post.Rd

Lines changed: 52 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/blog.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Create a blog post
2+
3+
Code
4+
new_blog_post("Intro to Felt Surrogacy", data = "1999-12-31", open = FALSE)
5+
Condition
6+
Error in `new_blog_post()`:
7+
! There is already a 'intro-to-felt-surrogacy' directory in 'posts/'
8+
9+
# Error if not a quarto project
10+
11+
Code
12+
new_blog_post("Intro to Felt Surrogacy", open = FALSE)
13+
Condition
14+
Error in `new_blog_post()`:
15+
! You need to be at root of a Quarto project to create a blog post in the 'posts/' directory at '<project directory>'.
16+
17+
---
18+
19+
Code
20+
new_blog_post("Intro to Felt Surrogacy", wd = tmp_dir, open = FALSE)
21+
Condition
22+
Error in `new_blog_post()`:
23+
! You need to be at root of a Quarto project to create a blog post in the 'posts/' directory at '<project directory>'.
24+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Create a blog post
2+
3+
Code
4+
new_blog_post("Intro to Felt Surrogacy", data = "1999-12-31", open = FALSE)
5+
Condition
6+
Error in `new_blog_post()`:
7+
! There is already a `intro-to-felt-surrogacy` directory in 'posts/'
8+

0 commit comments

Comments
 (0)