12.1 ソースコードを検閲する

ときにはレポートにソースコードの全文を掲載したくないこともあるでしょう. 例えばコードのある行にパスワードが書かれているかもしれません. 11.7節ではチャンクオプション echo で R コードの文ごとに表示の有無を選べることを言及しました. 例えば echo = 2 で2つ目の文を表示します. この節では, コードのインデックスを指定する必要のない, より柔軟な方法を提供します.

基本的なアイディアはコードに特殊なコメント, 例えば # 秘密!! のようなものを追加するということです. このコメントがコードのある行から検出されると, 行を省略します. 以下は source フックを使用した完全な例です.

---
title: "`source` フックを使用してコードのある行を隠す"
---

初めに, 末尾に `# 秘密!!` という文字列を含むコードの行を排除する `source` フックを用意します.

```{r, include=FALSE}
local({
  hook_source <- knitr::knit_hooks$get('source')
  knitr::knit_hooks$set(source = function(x, options) {
    x <- x[!grepl('# 秘密!!$', x)]
    hook_source(x, options)
  })
})
```

これで新しいフックをテストできます. この文書を knit すると, 特殊なコメント `# 秘密!!` のある行が見えなくなります.

```{r}
1 + 1  # 表示されるべき通常のコード

# 実際のユーザー名とパスワードを使ってみてください
auth <- httr::authenticate("user", "passwd")
auth <- httr::authenticate("yihui", "horsebattery")  # 秘密!!
httr::GET("http://httpbin.org/basic-auth/user/passwd", auth)
```

上記の source フックの重要な部分はこの行です. これはソースコードの入ったベクトル x から grepl() で追跡用のコメントの # 秘密!! とマッチングしたものを排除しています.

x <- x[!grepl("# SECRET!!$", x)]

正確に言うなら, 上記のフックは追跡用の # 秘密!! というコメントのある行ではなく, 評価式 (expressions) を全て排除します. x は実際には R 評価式のベクトルだからです. 例えば以下のコードチャンクを考えます.

1 + 1
if (TRUE) {
  # SECRET!!
  1:10
}

source フック内の x の値はこうなります.

c("1 + 1", "if (TRUE) { # SECRET!!\n  1:10\n}")

R 評価式ではなく行単位で隠したいなら, x を行ごとに分割しなければなりません. xfun::split_lines() の使用を検討するとよいでしょう. フック関数の本体はこうなります.

x <- xfun::split_lines(x)  # 個別の行に分割する
x <- x[!grepl("# SECRET!!$", x)]
x <- paste(x, collapse = "\n")  # 結合して1つの行にする
hook_source(x, options)

この例はソースコードの文字列を操作する方法を, そして grepl() は決して文字列操作の唯一の方法ではない, ということを示しています. 12.2節では他の例もお見せしています.