ob-jq.el (5718B)
1 ;;; ob-jq.el --- org-babel functions for jq scripts -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2015 Bjarte Johansen 4 5 ;; Author: Bjarte Johansen 6 ;; Keywords: literate programming, reproducible research 7 ;; Homepage: http://www.github.com/ljos/jq-mode 8 ;; Version: 0.1.0 9 10 ;;; License: 11 12 ;; This program is free software; you can redistribute it and/or modify 13 ;; it under the terms of the GNU General Public License as published by 14 ;; the Free Software Foundation; either version 3, or (at your option) 15 ;; any later version. 16 ;; 17 ;; This program is distributed in the hope that it will be useful, 18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 ;; GNU General Public License for more details. 21 ;; 22 ;; You should have received a copy of the GNU General Public License 23 ;; along with jq-mode. If not, see <http://www.gnu.org/licenses/>. 24 25 ;;; Commentary: 26 27 ;; Provides a way to evaluate jq scripts in org-mode. 28 29 ;;; Usage: 30 31 ;; Add to your Emacs config: 32 33 ;; (org-babel-do-load-languages 34 ;; 'org-babel-load-languages 35 ;; '((jq . t))) 36 37 ;;; Code: 38 (require 'ob) 39 (require 'jq-mode) 40 (require 'json) 41 42 (defvar org-babel-jq-command "jq" 43 "Name of the jq executable command.") 44 45 (defvar org-babel-tangle-lang-exts) 46 (add-to-list 'org-babel-tangle-lang-exts '("jq" . "jq")) 47 48 (defconst org-babel-header-args:jq 49 '( 50 (:in-file . :any) 51 (:cmd-line . :any) 52 (:compact . ((yes no))) 53 ) 54 "Jq specific header arguments.") 55 56 (defvar org-babel-default-header-args:jq '( 57 (:results . "output") 58 (:compact . "no") 59 ) 60 "Default arguments for evaluating a jq source block.") 61 62 (defun org-babel-jq-table-to-json (data) 63 "Convert org table to JSON. 64 65 First line specifies the keys." 66 (let* ((header (car data)) 67 (data (cdr data))) 68 (while (eq (car data) 'hline) 69 (setq data (cdr data))) 70 (json-encode 71 (mapcar 72 (lambda (row) (cl-mapcar 'cons header row)) 73 data)))) 74 75 (defun org-babel-jq-args (params) 76 "Return an --arg argument for each PARAMS :var" 77 (let ((vars (org-babel--get-vars params))) 78 (and vars 79 (mapconcat 80 (lambda (var) 81 (format "--arg %s %S" (car var) (cdr var))) 82 vars 83 " ")))) 84 85 (defun org-babel-execute:jq (body params) 86 "Execute a block of jq code with org-babel. This function is 87 called by `org-babel-execute-src-block'" 88 (message "executing jq source code block") 89 (let* ((result-params (cdr (assq :result-params params))) 90 (compact (equal "yes" (cdr (assq :compact params)))) 91 (cmd-line (cdr (assq :cmd-line params))) 92 (vars (org-babel-jq-args params)) 93 (in-file (cdr (assq :in-file params))) 94 (code-file (let ((file (org-babel-temp-file "jq-"))) 95 (with-temp-file file 96 (insert body) 97 file))) 98 (stdin (let ((stdin (cdr (assq :stdin params)))) 99 (when stdin 100 (let ((tmp (org-babel-temp-file "jq-stdin-")) 101 (res (org-babel-ref-resolve stdin))) 102 (with-temp-file tmp 103 (insert 104 (cond 105 ((listp res) (org-babel-jq-table-to-json res)) 106 (t res))) 107 tmp))))) 108 (cmd (mapconcat #'identity 109 (remq nil 110 (list org-babel-jq-command 111 (format "--from-file \"%s\"" code-file) 112 (when compact "--compact-output") 113 cmd-line 114 vars 115 in-file)) 116 " "))) 117 (org-babel-reassemble-table 118 (let ((results 119 (cond 120 (stdin (with-temp-buffer 121 (call-process-shell-command cmd stdin (current-buffer)) 122 (buffer-string))) 123 (t (org-babel-eval cmd ""))))) 124 (when results 125 (org-babel-result-cond result-params 126 results 127 (let ((data (json-read-from-string results))) 128 ;; If we have an array we might have a table 129 (if (and (vectorp data) 130 (> (length data) 0)) 131 (cond 132 ;; If the first element is a vector then just "unpack" 133 ;; the vector of vectors 134 ((vectorp (aref data 0)) 135 (mapcar (lambda (row) (append row nil)) data)) 136 ;; If the first element is a list we will assume we 137 ;; have an array of objects, so generate the colnames 138 ;; accordingly 139 ((consp (aref data 0)) 140 (let ((colnames (mapcar 'car (aref data 0)))) 141 (unless (assq :colnames params) 142 (push `(:colnames . ,colnames) params)) 143 (mapcar (lambda (row) (mapcar 'cdr row)) data))) 144 ;; For a vector of scalars just return it as an 145 ;; array, it will make a single-row table 146 (t (list (append data nil)))) 147 ;; If we have an object then just output it as string 148 results))))) 149 (org-babel-pick-name (cdr (assq :colname-names params)) 150 (cdr (assq :colnames params))) 151 (org-babel-pick-name (cdr (assq :rowname-names params)) 152 (cdr (assq :rownames params)))))) 153 154 (provide 'ob-jq) 155 ;;; ob-jq.el ends here