Skip to content

Commit

Permalink
Add inline prelude loading
Browse files Browse the repository at this point in the history
  • Loading branch information
federicotdn committed Dec 21, 2024
1 parent d58b7be commit 0e62e40
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 32 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ New features / improvements:
- Headers can now contain code tags that expand to multiple lines.
- URLs can now span multiple lines, place '\\' at the end of the URL line to continue it in the next one. Leading whitespace in the additional lines will be skipped.
- Added new function `verb-body-lf-to-crlf` designed for use with requests sending multipart data.
- Added new `Verb-Prelude` heading property, which can be used to specify an Emacs Lisp or JSON file to load variables from, before performing requests.
- Added new `Verb-Prelude` heading property, which can be used to specify Emacs Lisp or JSON contents to load variables from, before performing requests. Contents can come optionally from files, e.g. `.el` or `.json`.
- Added `verb-shell`, `verb-url` and `verb-unix-epoch` utility functions.
- Allow using Org [hyperlinks](https://orgmode.org/guide/Hyperlinks.html) in URLs, for example: `get [[http://example.com][my example]]`.

Expand Down
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,9 +444,9 @@ All of these are explained in later sections of this guide.
> [!NOTE]
> When reading Org heading properties, properties defined in parent headings are ignored by default (i.e. they are not inherited or passed down). This can be controlled using the `org-use-property-inheritance` variable (default: `nil`).
### Verb Variables from External Files
### Verb Variables from Preludes

To further keep sensitive information safe and separate from Verb `.org` files, Verb variables can also be defined from either JSON or Emacs Lisp external files. Use the `Verb-Prelude` property followed by the path (relative to the current Org file, or absolute) of the external file to load. The file will loaded and applied as a prelude before requests are sent.
To further keep sensitive information safe and separate from Verb `.org` files, Verb variables can also be defined from either JSON or Emacs Lisp external files. Use the `Verb-Prelude` property followed by the path (relative to the current Org file, or absolute) of the external file to load. The file will loaded and applied as a prelude before requests are sent. See the next section to learn how to load preludes directly from the `.org` file as well (inline).

> [!NOTE]
> Files that are GPG or EasyPG encrypted can opened and decrypted automatically by Emacs if configured appropriately. See [Emacs Auth-source manual](https://www.gnu.org/software/emacs/manual/auth.html) for more information. It is strongly recommended to use GPG or EasyPG when storing credentials in files.
Expand Down Expand Up @@ -493,8 +493,8 @@ For JSON prelude files, values (and first level sub-values) are set using `verb-

```json
{
"email": "[email protected]",
"user": "max_mustermann",
"email": "[email protected]",
"token": "abcdef123456",
"env_ids": {
"prod": "aaa111",
Expand All @@ -506,14 +506,39 @@ For JSON prelude files, values (and first level sub-values) are set using `verb-
Would result in the following Verb variables being set:

```text
email: [email protected]
user: max_musterman
email: [email protected]
token: abcdef123456
env_ids: (:prod aaa111 :dev zzz999) ; this is a plist
prod: aaa111
dev: zzz999
```

### Inline Preludes

If you do not with to keep your Verb `.org` file separate from your variable declarations, it is also possible to specify either Emacs Lisp or JSON content directly in the `Verb-Prelude` property. For example, for Emacs Lisp:

```text
:properties:
:Verb-Prelude+: (verb-set-var "user" "max_mustermann")
:Verb-Prelude+: (verb-set-var "email" user-mail-address)
:end:
```

And for JSON:

```text
:properties:
:Verb-Prelude+: {
:Verb-Prelude+: "user": "max_mustermann",
:Verb-Prelude+: "email": "[email protected]"
:Verb-Prelude+: }
:end:
```

> [!IMPORTANT]
> Note the `+` suffix for the `Verb-Prelude` property; this tells Org mode to collect every line of the property into a multiline string, instead of just one.
### Last Response

If you wish to access the last response's attributes, use the `verb-last` variable (type: `verb-response`). The following example does this; add it to the ending of your `guide.org` file:
Expand Down
2 changes: 1 addition & 1 deletion test/data/test.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"foo": "hello",
"bar": "world"
"bar": "world-json"
}
13 changes: 13 additions & 0 deletions test/data/test.org
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,24 @@ bar2
:Verb-Prelude: test.el
:end:
get /echo-args?{{(verb-var foo)}}={{(verb-var bar)}}
** prelude-elisp-inline
:properties:
:Verb-Prelude+: (setq prelude-inline-1 "abc")
:Verb-Prelude+: (setq prelude-inline-2 "321")
:end:
get /echo-args?{{prelude-inline-1}}={{prelude-inline-2}}
** prelude-json
:properties:
:Verb-Prelude: test.json
:end:
get /echo-args?{{(verb-var foo)}}={{(verb-var bar)}}
** prelude-json-inline
:properties:
:Verb-Prelude+: {
:Verb-Prelude+: "json-inline-var": "jsoninline"
:Verb-Prelude+: }
:end:
get /echo-args?value={{(verb-var json-inline-var)}}
* connection-fail-port
# Valid host but invalid port
get http://localhost:1234/test
Expand Down
12 changes: 10 additions & 2 deletions test/verb-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -2244,11 +2244,19 @@
(ert-deftest test-server-request-prelude-elisp ()
(let ((verb-suppress-load-unsecure-prelude-warning t))
(server-test "prelude-elisp"
(should (string= (buffer-string) "hello=world")))))
(should (string= (buffer-string) "hello=world")))))

(ert-deftest test-server-request-prelude-elisp-inline ()
(server-test "prelude-elisp-inline"
(should (string= (buffer-string) "abc=321"))))

(ert-deftest test-server-request-prelude-json ()
(server-test "prelude-json"
(should (string= (buffer-string) "hello=world"))))
(should (string= (buffer-string) "hello=world-json"))))

(ert-deftest test-server-request-prelude-json-inline ()
(server-test "prelude-json-inline"
(should (string= (buffer-string) "value=jsoninline"))))

(ert-deftest test-server-response-big5 ()
(server-test "response-big5"
Expand Down
69 changes: 45 additions & 24 deletions verb.el
Original file line number Diff line number Diff line change
Expand Up @@ -1185,8 +1185,25 @@ This affects only the current buffer."
(yes-or-no-p "Unset all Verb variables for current buffer? "))
(setq verb--vars nil)))

(defun verb--load-prelude (filename)
"Load Emacs Lisp or JSON configuration file FILENAME into Verb variables."
(defun verb--load-prelude (prelude)
"Load prelude PRELUDE from a file, or from string contents.
PRELUDE is interpreted as a filename if and only if it is a single-line
string containing no parenthesis nor curly brackets."
(if (and (= 1 (length (split-string prelude "\n")))
(not (string-match-p "[(){}]" prelude)))
(verb--load-prelude-from-file prelude)
(verb--load-prelude-from-string prelude)))

(defun verb--load-prelude-from-string (value)
"Load Emacs Lisp or JSON prelude data from VALUE.
First, try to read VALUE as JSON. If that fails, evaluate the code as
Emacs Lisp."
(condition-case nil
(verb--process-json-prelude value)
(t (verb--eval-string value))))

(defun verb--load-prelude-from-file (filename)
"Load Emacs Lisp or JSON prelude file FILENAME into Verb variables."
(interactive)
(save-excursion
(let ((file-extension (file-name-extension filename)))
Expand All @@ -1202,30 +1219,34 @@ This affects only the current buffer."
"\nLoad it anyways? ")))
(load-file filename)
(user-error "Operation cancelled")))
((string-match-p "^json.*" file-extension) ; file is JSON(C)
(let* ((file-contents
(with-temp-buffer
(insert-file-contents filename)
(set-auto-mode)
(goto-char (point-min))
;; If a modern JSON / JavaScript package not
;; installed, then comments cannot be removed or
;; supported. Also, not likely to have JSON comments
;; if this is the case.
(when comment-start
(comment-kill (count-lines (point-min) (point-max))))
(verb--buffer-string-no-properties)))
(json-object-type 'plist)
(data (json-read-from-string file-contents)))
;; Search for values on the topmost container, and one level down.
(cl-loop for (k v) on data by #'cddr
do (verb-set-var (substring (symbol-name k) 1) v)
if (and (listp v) (cl-evenp (length v)))
do (cl-loop for (subk subv) on v by #'cddr
do (verb-set-var
(substring (symbol-name subk) 1) subv)))))
((string-match-p "^json.?" file-extension) ; file is JSON(C)
(let ((file-contents
(with-temp-buffer
(insert-file-contents filename)
(set-auto-mode)
(goto-char (point-min))
;; If a modern JSON / JavaScript package not
;; installed, then comments cannot be removed or
;; supported. Also, not likely to have JSON comments
;; if this is the case.
(when comment-start
(comment-kill (count-lines (point-min) (point-max))))
(verb--buffer-string-no-properties))))
(verb--process-json-prelude file-contents)))
(t (user-error "Unable to determine file type for %s" filename))))))

(defun verb--process-json-prelude (json-string)
"Process JSON-STRING and set Verb variables accordingly."
(let* ((json-object-type 'plist)
(data (json-read-from-string json-string)))
;; Search for values on the topmost container, and one level down.
(cl-loop for (k v) on data by #'cddr
do (verb-set-var (substring (symbol-name k) 1) v)
if (and (listp v) (cl-evenp (length v)))
do (cl-loop for (subk subv) on v by #'cddr
do (verb-set-var
(substring (symbol-name subk) 1) subv)))))

(defun verb-show-vars ()
"Show values of variables set with `verb-var' or `verb-set-var'.
Values correspond to variables set in the current buffer. Return the
Expand Down

0 comments on commit 0e62e40

Please sign in to comment.