2

GNU Emacs 26.0.50.2 (x86_64-pc-linux-gnu, GTK+ Version 3.18.9) of 2016-10-06
Org-mode version 8.3.5 (release_8.3.5-1131-g7eecb2 @ ~/.emacs.d/el-get/org-mode/lisp/)

I have come to share my projects as html files where I include notes and codes so that others can follow my workflow and reproduce my results.

One thing is that I want to reuse code blocks with different input variables, and I do this with the :var header arg. The issue is that :vararguments are not included in the exported code block.

Is it possible to selectively choose code blocks where the :var arguments are included in the exported code block? I noticed that when tangling the code block the variables are included in the file.

Note that I am asking for a language agnostic solution if possible.

Example use case:

cat ./export_var.org

* Export with variables
:PROPERTIES:
:header-args: :var status="not_finished"
:END:

 #+NAME: example-table
| 1 |
| 2 |
| 3 |
| 4 |

Here we import a table.          
#+NAME: table-length
#+BEGIN_SRC R :var table=example-table :tangle "./export_var.R"
status <- "finished"
dim(table)
#+END_SRC

#+RESULTS: table-length
| 4 |
| 1 |

Here we think we import a table
#+NAME: table-str-length
#+BEGIN_SRC R :var table="./filename_example-table.tsv" :tangle "./export_var.R"
length(table)
print(status)
#+END_SRC

#+RESULTS: table-str-length
: not_finished

** Some subcase
If I run with the data in ="./other_data.tsv"=
#+call: table-str-length[ :var table="./other_data.tsv"]()

#+RESULTS:
: not_finished

Now this would tangle as the following ./export_var.R as

status <- "not_finished"
table <- local({
     con <- textConnection(
       "\"1\"
\"2\"
\"3\"
\"4\""
     )
     res <- utils::read.table(
       con,
       header    = FALSE,
       row.names = NULL,
       sep       = "\t",
       as.is     = TRUE
     )
     close(con)
     res
   })
status <- "finished"
dim(table)

status <- "not_finished"
table <- "./filename_example-table.tsv"
length(table)
print(status)

The desired result would be the same code as in the tangled file, although split up as usual in the html file.

The inheritance structure of :header-args can be somewhat complex.

  • Did you only want variables to show in exported code or other code block headers too? – Melioratus Oct 6 '16 at 17:36
  • 1
    @Melioratus my idea is that the exported code should be similar to the tangled code. that the variable related to the code block would be prepended so that if someone where to copy paste the exported code it would run with minimal effort. – Sparx Oct 6 '16 at 18:11
  • That's what I hoped you say! Thanks for clarification! I know how to do what you want. I'll post my answer soon. – Melioratus Oct 6 '16 at 20:21
  • 1
    If you know elisp, perhaps you could use org-babel-expand-src-block as a template to enhance :export header. To see org-babel-expand-src-block in action, click inside your code block and type C-c C-v C-v and your code will/should be displayed with variables in a new frame. Unfortunately, I can't figure out how to capture the expanded buffer and insert it back into the org file. I'll keep looking though. – Melioratus Nov 12 '16 at 0:26
  • 1
    I did it!! org-babel-expand-src-block contained the answer! I'm writing up some language agnostic examples for a new answer. Thank you so much for asking the question! – Melioratus Nov 23 '16 at 20:21
3

Add Code to Org file

expand-src-block

#+NAME: expand-src-block
#+BEGIN_SRC elisp :var block-name="" :var datum="" :var info="" :var lang="" :var body="" :exports none
  (save-excursion
    (org-babel-goto-named-src-block block-name)
    (setq datum (org-element-at-point))
    t)
  (setq info (org-babel-get-src-block-info nil datum))
  (setq lang (nth 0 info))
  (setq body (org-babel-expand-src-block nil info))
  (format "%s" body)
#+END_SRC

Usage Examples

General

  • Use noweb Syntax to Export Code with Variables

    1. Assign original code block a name using #+NAME:.

      #+NAME: print-abc
      #+BEGIN_SRC shell :var data="ABC"
        echo -n $data
      #+END_SRC
      
    2. Prevent original code block from exporting using :exports none.

      #+NAME: print-abc
      #+BEGIN_SRC shell :var data="ABC" :exports none
        echo -n $data
      #+END_SRC
      
    3. Create new noweb code block under original block.

      #+NAME: print-abc
      #+BEGIN_SRC shell :var data="ABC" :exports none
        echo -n $data
      #+END_SRC
      
      #+BEGIN_SRC shell :noweb yes :exports both 
       <<expand-src-block(block-name="print-abc")>>
      #+END_SRC
      

      Exported output should be similar to the following:

      data='ABC'
      echo -n $data
      
      ABC
      

Question Specific Use Case

Added expand-src-block code, :exports none headers and noweb code blocks to your original example use case.

#+NAME: expand-src-block
#+BEGIN_SRC elisp :var block-name="" :var datum="" :var info="" :var lang="" :var body="" :exports none
  (save-excursion
    (org-babel-goto-named-src-block block-name)
    (setq datum (org-element-at-point))
    t)
  (setq info (org-babel-get-src-block-info nil datum))
  (setq lang (nth 0 info))
  (setq body (org-babel-expand-src-block nil info))
  (format "%s" body)
#+END_SRC

* Export with variables
:PROPERTIES:
:header-args: :var status="not_finished"
:END:

#+NAME: example-table
| 1 |
| 2 |
| 3 |
| 4 |

Here we import a table.          
#+NAME: table-length
#+BEGIN_SRC R :var table=example-table :tangle "./export_var.R" :exports none
status <- "finished"
dim(table)
#+END_SRC

# Export the expanded code
#+BEGIN_SRC R :noweb yes :exports both 
 <<expand-src-block(block-name="table-length")>>
#+END_SRC

Here we think we import a table
#+NAME: table-str-length
#+BEGIN_SRC R :var table="./filename_example-table.tsv" :tangle "./export_var.R" :exports none
length(table)
print(status)
#+END_SRC

# Export the expanded code
#+BEGIN_SRC R :noweb yes :exports both 
 <<expand-src-block(block-name="table-str-length")>>
#+END_SRC

*** Some subcase

If I run with the data in ="./other_data.tsv"=
#+call: table-str-length[ :var table="./other_data.tsv"]()

Exported results should be similar to the following:

* Export with variables
#+NAME: example-table
| 1 |
| 2 |
| 3 |
| 4 |

Here we import a table.          
#+BEGIN_SRC R
  table <- local({
       con <- textConnection(
     "\"1\"
  \"2\"
  \"3\"
  \"4\""
       )
       res <- utils::read.table(
     con,
     header    = FALSE,
     row.names = NULL,
     sep       = "\t",
     as.is     = TRUE
       )
       close(con)
       res
     })
  status <- "finished"
  dim(table)
#+END_SRC

#+RESULTS: 
| 4 |
| 1 |

Here we think we import a table
#+BEGIN_SRC R
  table <- "./filename_example-table.tsv"
  length(table)
  print(status)
#+END_SRC

#+RESULTS: 
: not_finished

*** Some subcase

If I run with the data in ="./other_data.tsv"=
#+RESULTS: 
: not_finished

Test Info

Org Source Code

This code was tested using
GNU Emacs 24.5.1 (x86_64-unknown-cygwin, GTK+ Version 3.14.13)
org-mode version: 9.0

  • This was a really excellent solution! Thanks for the effort. Sorry, hectic period so not been quick to respond. Just got time to try it out and it seems to work really nicely. Additionally it also solves another worry I had of really big tables as variables but this makes it possible to combine src blocks as wished so large tables can be excluded from the export src block. Finally i can make really reproducible document exports. – Sparx Dec 4 '16 at 23:57
  • @Sparx - Thanks! I've learned a lot by working on your question. BTW, I'm still working on the #+CALL: variant of the answer which has simpler syntax but is throwing an error during export so I didn't include the example with my answer yet. – Melioratus Dec 5 '16 at 0:21
  • Totally forgot my second question there. It would be nice if the call syntax would also export. But I guess you at least need to name the call block then? – Sparx Dec 6 '16 at 23:10
2

Embed org-babel-variable-assignments:R call into R code block using elisp and noweb

  1. Create a named elisp block, e.g. convert-data-to-R-code, without :var headers that returns results from org-babel-variable-assignments:R function.

    #+NAME: convert-data-to-R-code
    #+BEGIN_SRC elisp  :exports none 
      (mapconcat 'identity (org-babel-variable-assignments:R params) " ")
    #+END_SRC
    
  2. Call elisp block, convert-data-to-R-code, in R code block during export via noweb.

    Note: Pass original :var headers inside square brackets [] to elisp code block, e.g. :var table"./filename_example-table.tsv"

    #+BEGIN_SRC R  :noweb yes 
    <<convert-data-to-R-code[ :var table="./filename_example-table.tsv"]()>>
    dim(table)
    #+END_SRC
    

    After exporting, the R code should contain missing variable assignments.

    table <- "./filename_example-table.tsv"
    dim(table)
    

Hope that helped

This code was tested using:
GNU Emacs 24.5.1 (x86_64-unknown-cygwin, GTK+ Version 3.14.13)
org-mode version: 8.3.5


Show Org Source

  • 1
    Thanks for the input! I am wondering some things though. How is this different from setting the variable in the src block directly? Also I have issues with the params variable not being defined when I try to run this example. I'm going to update my question a bit, becouse I think I might have been to unclear. – Sparx Oct 7 '16 at 9:08
  • 1
    @Sparx - In answer to your question, I've only found the technique useful when auto generating test code. I was looking for way to create language agnostic closures at the time. I'm not sure why params isn't working, sorry. I'll push my original org file to GitHub so you can try exporting from the original. Perhaps a problem was introduced when exported my answer as markdown. – Melioratus Oct 8 '16 at 16:32
  • 2
    @Sparx - Added a link to org file I used to generate my answer, in case you want to test why params not working. – Melioratus Oct 11 '16 at 22:43
  • :( cannot realize how to repeat this for SQL. seems there are no org-babel-variable-assignments:sql function – Dima Fomin Nov 7 '17 at 18:45
  • @DimaFomin can you post sql issue as new question and I’ll take a look. Might have a workaround if not a fix – Melioratus Nov 7 '17 at 18:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.