Pod :code field

This commit is contained in:
Michiel Borkent 2020-05-17 16:32:05 +02:00
parent 5f384d8540
commit 451d5211ed
3 changed files with 55 additions and 26 deletions

@ -1 +1 @@
Subproject commit 8f08cd7bc8f6c89880cbc708b15889c25b32277b
Subproject commit 903489fcb5d23fe1ebf4f6e1e321419ed3739268

@ -1 +1 @@
Subproject commit 016e20dc52d553049694d7496602dbbccbd6a949
Subproject commit 8f16139ab6e73a91aeb5fe97b398b30670e3fc9b

View file

@ -1,7 +1,7 @@
# Pods
Pods are standalone programs that can expose namespaces with vars to
babashka. Pods can be created independently from babashka. Any program can be
Pods are standalone programs that can expose namespaces with vars to babashka or
a JVM. Pods can be created independently from babashka. Any program can be
invoked as a pod as long as it implements the _pod protocol_. This protocol is
influenced by and built upon battle-tested technologies:
@ -36,6 +36,8 @@ _below_ in Polish and Russian. In Romanian it means _bridge_
## Implementing your own pod
We will refer to babashka or the JVM, invoking the pod, as the pod client.
### Examples
Beyond the already available pods mentioned above, eductional examples of pods
@ -77,7 +79,7 @@ Pods created by the babashka maintainers use the identifier `babashka`:
#### Message and payload format
Exchange of _messages_ between babashka and the pod happens in the
Exchange of _messages_ between pod client and the pod happens in the
[bencode](https://en.wikipedia.org/wiki/Bencode) format. Bencode is a bare-bones
format that only has four types:
@ -105,12 +107,12 @@ might not be a good EDN library available. So we use bencode as the first
encoding and choose one of multiple richer encodings on top of this. More
payload formats might be added in the future (e.g. transit).
When calling the `babashka.pods/load-pod` function, babashka will start the pod
and leave the pod running throughout the duration of a babashka script.
When calling the `babashka.pods/load-pod` function, the pod client will start
the pod and leave the pod running throughout the duration of a babashka script.
#### describe
The first message that babashka will send to the pod on its stdin is:
The first message that the pod client will send to the pod on its stdin is:
``` clojure
{"op" "describe"}
@ -137,10 +139,10 @@ In this reply, the pod declares that payloads will be encoded and decoded using
JSON. It also declares that the pod exposes one namespace,
`pod.lispyclouds.sqlite` with one var `execute!`.
The pod encodes the above map to bencode and writes it to stdoud. Babashka reads
this message from the pod's stdout.
The pod encodes the above map to bencode and writes it to stdoud. The pod client
reads this message from the pod's stdout.
Upon receiving this message, babashka creates these namespaces and vars.
Upon receiving this message, the pod client creates these namespaces and vars.
The optional `ops` value communicates which ops the pod supports, beyond
`describe` and `invoke`. It is a map of op names to option maps. In the above
@ -148,7 +150,7 @@ example the pod declares that it supports the `shutdown` op. Since the
`shutdown` op does not need any additional options right now, the value is an
empty map.
As a babashka user, you can load the pod with:
As a pod user, you can load the pod with:
``` clojure
(require '[babashka.pods :as pods])
@ -162,13 +164,13 @@ As a babashka user, you can load the pod with:
#### invoke
When invoking a var that is related to the pod, let's call it a _proxy var_,
babashka reaches out to the pod with the arguments encoded in JSON or EDN. The
pod will then respond with a return value encoded in JSON or EDN. Babashka will
then decode the return value and present the user with that.
When invoking a var that is related to the pod, let's call it a _proxy var_, the
pod client reaches out to the pod with the arguments encoded in JSON or EDN. The
pod will then respond with a return value encoded in JSON or EDN. The pod client
will then decode the return value and present the user with that.
Example: the user invokes `(sql/execute! "select * from foo")`. Babashka sends
this message to the pod:
Example: the user invokes `(sql/execute! "select * from foo")`. The pod client
sends this message to the pod:
``` clojure
{"id" "1d17f8fe-4f70-48bf-b6a9-dc004e52d056"
@ -176,7 +178,7 @@ this message to the pod:
"args" "[\"select * from foo\"]"
```
The `id` is unique identifier generated by babashka which correlates this
The `id` is unique identifier generated by the pod client which correlates this
request with a response from the pod.
An example response from the pod could look like:
@ -188,22 +190,22 @@ An example response from the pod could look like:
```
Here, the `value` payload is the return value of the function invocation. The
field `status` contains `"done"`. This tells babashka that this is the last
field `status` contains `"done"`. This tells the pod client that this is the last
message related to the request with `id` `1d17f8fe-4f70-48bf-b6a9-dc004e52d056`.
Now you know most there is to know about the pod protocol!
#### shutdown
When babashka is about to exit, it sends an `{"op" "shutdown"}` message, if the
When the pod client is about to exit, it sends an `{"op" "shutdown"}` message, if the
pod has declared that it supports it in the `describe` response. Then it waits
for the pod process to end. This gives the pod a chance to clean up resources
before it exits. If the pod does not support the `shutdown` op, the pod process
is killed by babashka.
is killed by the pod client.
#### out and err
Pods may send messages with an `out` and `err` string value. Babashka prints
Pods may send messages with an `out` and `err` string value. The Pod Client prints
these messages to `*out*` and `*err*`. Stderr from the pod is redirected to
`System/err`.
@ -220,7 +222,7 @@ these messages to `*out*` and `*err*`. Stderr from the pod is redirected to
#### Error handling
Responses may contain an `ex-message` string and `ex-data` payload string (JSON
or EDN) along with an `"error"` value in `status`. This will cause babashka to
or EDN) along with an `"error"` value in `status`. This will cause the pod client to
throw an `ex-info` with the associated values.
Example:
@ -245,7 +247,7 @@ time in the future. Async functions must be declared as such as part of the
"vars" [{"name" "watch" "async" "true"}]}]}
```
When calling this function from babashka, the return value is a `core.async`
When calling this function from the pod client, the return value is a `core.async`
channel on which the values will be received:
``` clojure
@ -259,8 +261,35 @@ channel on which the values will be received:
#### Environment
Babashka will set the `BABASHKA_POD` environment variable to `true` when
The pod client will set the `BABASHKA_POD` environment variable to `true` when
invoking the pod. This can be used by the invoked program to determine whether
it should behave as a pod or not.
Added in v0.0.94.
#### Client side code
Pods may implement functions and macros by sending arbitrary code to the pod
client in a `"code"` field as part of a `"var"` section. The code is evaluated
by the pod client inside the declared namespace.
For example, a pod can define a macro called `do-twice`:
``` clojure
{"format" "json"
"namespaces"
[{"name" "pod.babashka.demo"
"vars" [{"name" "do-twice" "code" "(defmacro do-twice [x] `(do ~x ~x))"}]}]}
```
In the pod client:
``` clojure
(pods/load-pod "pod-babashka-demo")
(require '[pod.babashka.demo :as demo])
(demo/do-twice (prn :foo))
;;=>
:foo
:foo
nil
```