knitr and ClojureThis document is rendered by R knitr package with embedded Clojure code. Yes, it’s possible. The renderer is configured to use nRepl client: rep.
Knitr is R package which generates really variety documents out of markdown file with embedded code.
First let’s define data.
(def data {:a [1 2 3]
:b [3 4 5]})
data#'user/data
{:a [1 2 3], :b [3 4 5]}
Code was executed, data is defined and we can run another chunk.
(keys data)(:a :b)
And another one (everything is kept in user namespace).
(->> data
vals
(apply concat)
(reduce +))18
(clojure.pprint/pprint (repeatedly 6 #(repeatedly 3 rand)))((0.7384181016584196 0.9022926873284335 0.16387284419405546)
(0.7876579288875423 0.278112269422367 0.6447338227322267)
(0.992631672548641 0.22538393600791107 0.9135077552206766)
(0.8608119263066379 0.11505334245887577 0.4759075447034199)
(0.9554866649873992 0.6662879286006599 0.8202240867647573)
(0.48578404443201617 0.5443761228440936 0.40380083568888214))
(require '[clojure2d.core :refer [save]]
'[clojure2d.color :as c]
'[clojure2d.extra.utils :as u])(-> :cubehelix
(c/gradient)
(u/gradient->image true)
(save "gradient.png"))Generated gradient with luma
(println "
test/data/stocks.csv [5 3]:
| symbol | date | price |
|--------+------------+-------|
| MSFT | 2000-01-01 | 39.81 |
| MSFT | 2000-02-01 | 36.35 |
| MSFT | 2000-03-01 | 43.22 |
| MSFT | 2000-04-01 | 28.37 |
| MSFT | 2000-05-01 | 25.45 |
")test/data/stocks.csv [5 3]:
| symbol | date | price |
|---|---|---|
| MSFT | 2000-01-01 | 39.81 |
| MSFT | 2000-02-01 | 36.35 |
| MSFT | 2000-03-01 | 43.22 |
| MSFT | 2000-04-01 | 28.37 |
| MSFT | 2000-05-01 | 25.45 |
I’m using Emacs with CIDER here.
repnReplknitr and rmarkdown packages (and all needed deps, like pandoc)ESS, poly-R package which enables REPL inside Markdown file.Run nRepl, create .Rmd file and add below chunk at the beginning of it. As you can see, there is a place to define nrepl_port. Find your port and change this value. I haven’t been able to find an easy way to setup it automatically (yet).
```{r setup, include=FALSE}
find_nrepl_port_up <- function() {
wd <- getwd()
while(wd != dirname(wd)) {
f <- paste0(wd,"/.nrepl-port")
if(file.exists(f)) return(paste0("@",f))
wd <- dirname(wd)
f <- NULL
}
}
port_file <- find_nrepl_port_up()
if(is.null(port_file)) stop("nREPL port not found")
library(knitr)
knitr_one_string <- knitr:::one_string
nrepl_cmd <- "rep"
opts_chunk$set(comment=NA, highlight=TRUE)
knit_engines$set(clojure = function(options) {
rep_params <- if((options$results == "asis") || isTRUE(options$stdout_only)) {
"--print 'out,1,%{out}' --print 'value,1,' -p"
} else {
"-p"
}
code <- paste(rep_params, port_file, shQuote(knitr_one_string(options$code)))
out <- if (options$eval) {
if (options$message) message('running: ', nrepl_cmd, ' ', code)
tryCatch(
system2(nrepl_cmd, code, stdout = TRUE, stderr = TRUE, env = options$engine.env),
error = function(e) {
if (!options$error) stop(e)
paste('Error in running command', nrepl_cmd)
}
)
} else ''
if (!options$error && !is.null(attr(out, 'status'))) stop(knitr_one_string(out))
engine_output(options, options$code, out)})
```When it’s done you can generate documents (html, pdf, whatever) within ESS or from external R session.
library(rmarkdown)
render("README.Rmd","all")Emacs in action
There are couple of problems: