Compare commits
No commits in common. "master" and "v0.0.1" have entirely different histories.
21 changed files with 310 additions and 870 deletions
|
|
@ -1,28 +0,0 @@
|
|||
version: 2.1
|
||||
jobs:
|
||||
test:
|
||||
docker:
|
||||
- image: clojure:openjdk-11-tools-deps-1.10.3.1087-slim-bullseye
|
||||
working_directory: ~/repo
|
||||
environment:
|
||||
LEIN_ROOT: "true"
|
||||
BABASHKA_PLATFORM: linux
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "deps.edn" }}
|
||||
# fallback to using latest cache if no exact match is found
|
||||
- v1-dependencies-
|
||||
- run: |
|
||||
script/test
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
key: v1-dependencies-{{ checksum "deps.edn" }}
|
||||
workflows:
|
||||
version: 2
|
||||
ci:
|
||||
jobs:
|
||||
- test
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -3,7 +3,3 @@
|
|||
.lein-failures
|
||||
/pom.xml
|
||||
.lein-repl-history
|
||||
.cache
|
||||
.clj-kondo/babashka
|
||||
.clj-kondo/rewrite-clj
|
||||
src/scratch.clj
|
||||
|
|
|
|||
21
CHANGELOG.md
21
CHANGELOG.md
|
|
@ -1,21 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
- [#63](https://github.com/babashka/pods/issues/63): create directory before un-tarring
|
||||
- [#59](https://github.com/babashka/pods/issues/59): delete port file on exit
|
||||
- [#65](https://github.com/babashka/pods/issues/65): fix warnings when defining var with core name in JVM
|
||||
- [#66](https://github.com/babashka/pods/issues/66): Allow metadata on fn arguments for transit+json
|
||||
|
||||
## v0.2.0
|
||||
|
||||
- [#61](https://github.com/babashka/pods/issues/61): add transit as explicit JVM dependency
|
||||
- [#60](https://github.com/babashka/pods/issues/60): transform pod reader error into exception of caller
|
||||
- Switch "out" and "err" messages to print and flush instead of `println` ([@justone](https://github.com/justone))
|
||||
- Set TCP_NODELAY on transport socket ([@retrogradeorbit](https://github.com/retrogradeorbit))
|
||||
- Allow env vars OS_NAME & OS_ARCH to override os props ([@cap10morgan](https://github.com/cap10morgan))
|
||||
- [#49](https://github.com/babashka/pods/issues/49): don't log socket closed exception
|
||||
|
||||
## v0.1.0
|
||||
|
||||
Initial version
|
||||
104
README.md
104
README.md
|
|
@ -1,6 +1,4 @@
|
|||
# Babashka pods
|
||||
|
||||
[](https://clojars.org/babashka/babashka.pods)
|
||||
# babashka.pods
|
||||
|
||||
Babashka pods are programs that can be used as Clojure libraries by babashka.
|
||||
|
||||
|
|
@ -74,44 +72,7 @@ On the JVM:
|
|||
|
||||
### Where does the pod come from?
|
||||
|
||||
When calling `load-pod` with a string or vector of strings (or declaring it in your `bb.edn`),
|
||||
the pod is looked up on the local file system (either using the PATH, or using an absolute path).
|
||||
When it is called with a qualified symbol and a version - like `(load-pod 'org.babashka/aws "0.0.5")`
|
||||
then it will be looked up in and downloaded from the [pod-registry](https://github.com/babashka/pod-registry). You can customize the file system location that `load-pod` will use by setting the `BABASHKA_PODS_DIR` environment variable.
|
||||
|
||||
By default babashka will search for a pod binary matching your system's OS and arch. If you want to download
|
||||
pods for a different OS / arch (e.g. for deployment to servers), you can set one or both of the following
|
||||
environment variables:
|
||||
|
||||
- `BABASHKA_PODS_OS_NAME=Linux` (or `Mac OS X` or any other value returned by Java's `os.name` property)
|
||||
- `BABASHKA_PODS_OS_ARCH=aarch64` (or `amd64` or any other value returned by Java's `os.arch` property)
|
||||
|
||||
### In a babashka project
|
||||
|
||||
As of babashka 0.8.0 you can declare the pods your babashka project uses in your `bb.edn` file like so:
|
||||
|
||||
```clojure
|
||||
:pods {org.babashka/hsqldb {:version "0.1.0"} ; will be downloaded from the babashka pod registry
|
||||
my.local/pod {:path "../pod-my-local/my-pod-binary"
|
||||
:cache false}} ; optionally disable namespace caching if you're actively working on this pod
|
||||
```
|
||||
|
||||
Then you can just require the pods in your code like any other clojure lib:
|
||||
|
||||
```clojure
|
||||
(ns my.project
|
||||
(:require [pod.babashka.hsqldb :as sql]
|
||||
[my.local.pod :as my-pod]))
|
||||
|
||||
(def db "jdbc:hsqldb:mem:testdb;sql.syntax_mys=true")
|
||||
(sql/execute! db ["create table foo ( foo int );"])
|
||||
;;=> [#:next.jdbc{:update-count 0}]
|
||||
|
||||
(my-pod/do-a-thing "foo")
|
||||
;;=> "something"
|
||||
```
|
||||
|
||||
The pods will then be loaded on demand when you require them. No need to call `load-pod` explicitly.
|
||||
When calling `load-pod` with a string or vector of strings, the pod is looked up on the local file system (either using the PATH, or using an absolute path). When it is called with a qualified symbol and a version - like `(load-pod 'org.babashka/aws "0.0.5")` then it will be looked up in and downloaded from the [pod-registry](https://github.com/babashka/pod-registry).
|
||||
|
||||
## Sci
|
||||
|
||||
|
|
@ -130,7 +91,7 @@ light weight replacement for native interop (JNI, JNA, etc.).
|
|||
|
||||
### Examples
|
||||
|
||||
Beyond the already available pods mentioned above, educational examples of pods
|
||||
Beyond the already available pods mentioned above, eductional examples of pods
|
||||
can be found [here](examples):
|
||||
|
||||
- [pod-lispyclouds-sqlite](examples/pod-lispyclouds-sqlite): a pod that
|
||||
|
|
@ -228,7 +189,7 @@ JSON. It also declares that the pod exposes one namespace,
|
|||
|
||||
To encode payloads in EDN use `"edn"` and for Transit JSON use `"transit+json"`.
|
||||
|
||||
The pod encodes the above map to bencode and writes it to stdout. The pod client
|
||||
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, the pod client creates these namespaces and vars.
|
||||
|
|
@ -374,55 +335,6 @@ In the pod client:
|
|||
nil
|
||||
```
|
||||
|
||||
#### Metadata
|
||||
|
||||
**From pod to pod client**
|
||||
|
||||
*Fixed Metadata on vars*
|
||||
|
||||
Pods may attach metadata to functions and macros by sending data to the pod client
|
||||
in a `"meta"` field as part of a `"var"` section. The metadata must be an appropriate
|
||||
map, encoded as an EDN string. This is only applicable to vars in the pod and will be
|
||||
ignored if the var refers to Client-side code, since metadata can already be defined
|
||||
in those code blocks (see 'Dynamic Metadata' below to enable the encoding of metadata).
|
||||
|
||||
For example, a pod can define a function called `add`:
|
||||
|
||||
``` clojure
|
||||
{"format" "json"
|
||||
"namespaces"
|
||||
[{"name" "pod.babashka.demo"
|
||||
"vars" [{"name" "add"
|
||||
"meta" "{:doc \"arithmetic addition of 2 arguments\" :arglists ([a b])}"}]}]}
|
||||
```
|
||||
|
||||
*Dynamic Metadata*
|
||||
|
||||
Pods may send metadata on values returned to the client if metadata encoding is enabled
|
||||
for the particular transport format used by the pod.
|
||||
|
||||
For example, if your pod uses `:transit+json` as its format, you can enable metadata
|
||||
encoding by adding `:transform transit/write-meta` (or whatever transit is aliased to)
|
||||
to the optional map passed to `transit/writer`. e.g.:
|
||||
|
||||
````clojure
|
||||
(transit/writer baos :json {:transform transit/write-meta})
|
||||
````
|
||||
|
||||
##### From pod client to pod
|
||||
|
||||
Currently sending metadata on arguments passed to a pod function is available only for the
|
||||
`transit+json` format and can be enabled on a per var basis.
|
||||
|
||||
A pod can enable metadata to be read on arguments by sending the "arg-meta" field to "true"
|
||||
for the var representing that function. For example:
|
||||
|
||||
````clojure
|
||||
{:format :transit+json
|
||||
:namespaces [{:name "pod.babashka.demo"
|
||||
:vars [{"name" "round-trip" "arg-meta" "true"}]}]}
|
||||
````
|
||||
|
||||
#### Deferred namespace loading
|
||||
|
||||
When your pod exposes multiple namespaces that can be used independently from
|
||||
|
|
@ -497,7 +409,7 @@ The arguments to `babashka.pods/invoke` are:
|
|||
- a pod identifier string derived from the first described namespace.
|
||||
- the symbol of the var to invoke
|
||||
- the arguments to the var
|
||||
- an opts map containing `:handlers` containing callback functions: `:success`, `:error` and `:done`
|
||||
- an opts map containing `:handler` containing callback functions: `:success`, `:error` and `:done`
|
||||
|
||||
The return value of `babashka.pods/invoke` is a map containing `:result`. When
|
||||
not using callbacks, this is the return value from the pod var invocation. When
|
||||
|
|
@ -523,9 +435,9 @@ callback is only called if no errors were sent by the pod.
|
|||
|
||||
In the above example the wrapper function calls the pod identified by
|
||||
`"pod.babashka.filewatcher"`. It calls the var
|
||||
`pod.babashka.filewatcher/watch*`. In `:success` it pulls out received
|
||||
`pod.babashka.filewatcher/watch*`. In `:on-success` it pulls out received
|
||||
values, passing them to the user-provided callback. Additionally, it prints any
|
||||
errors received from the pod library in `:error` to `*err*`.
|
||||
errors received from the pod library in `:on-error` to `*err*`.
|
||||
|
||||
A user will then use `pod.babashka.filewatcher/watch` like this:
|
||||
|
||||
|
|
@ -542,7 +454,7 @@ user=> (fw/watch "/tmp" (fn [result] (prn "result" result)))
|
|||
nil
|
||||
user=> (spit "/tmp/foobar123.txt" "foo")
|
||||
nil
|
||||
user=> "result" {:path "/private/tmp/foobar123.txt", :type :create}
|
||||
user=> "result" {:path "/private/tmp/foobar123.txt", :type "create"}
|
||||
```
|
||||
|
||||
## Run tests
|
||||
|
|
|
|||
2
bb.edn
2
bb.edn
|
|
@ -1,2 +0,0 @@
|
|||
{:tasks {test {:doc "Run tests"
|
||||
:task (shell "script/test")}}}
|
||||
5
deps.edn
5
deps.edn
|
|
@ -1,7 +1,6 @@
|
|||
{:deps {nrepl/bencode {:mvn/version "1.1.0"}
|
||||
cheshire/cheshire {:mvn/version "5.10.0"}
|
||||
com.cognitect/transit-clj {:mvn/version "1.0.324"}
|
||||
babashka/fs {:mvn/version "0.1.6"}}
|
||||
com.cognitect/transit-clj {:mvn/version "1.0.324"}}
|
||||
:aliases
|
||||
{:sci
|
||||
{:extra-deps
|
||||
|
|
@ -19,5 +18,5 @@
|
|||
{lambdaisland/kaocha {:mvn/version "1.0.632"}}
|
||||
:main-opts ["-m" "kaocha.runner"]}
|
||||
:test-pod
|
||||
{:extra-paths ["src" "test-pod"]
|
||||
{:paths ["src" "test-pod"]
|
||||
:main-opts ["-m" "pod.test-pod"]}}}
|
||||
|
|
|
|||
14
project.clj
14
project.clj
|
|
@ -1,17 +1,15 @@
|
|||
(defproject babashka/babashka.pods "0.2.0"
|
||||
(defproject babashka/babashka.pods "0.0.1"
|
||||
:description "babashka pods"
|
||||
:url "https://github.com/babashka/babashka.pods"
|
||||
:scm {:name "git"
|
||||
:url "https://github.com/babashka/babashka.pods"}
|
||||
:license {:name "EPL-1.0"
|
||||
:url "https://www.eclipse.org/legal/epl-1.0/"}
|
||||
:dependencies [[org.clojure/clojure "1.10.3"]
|
||||
:dependencies [[org.clojure/clojure "1.10.2-alpha1"]
|
||||
[nrepl/bencode "1.1.0"]
|
||||
[cheshire "5.10.0"]
|
||||
[babashka/fs "0.1.6"]
|
||||
[com.cognitect/transit-clj "1.0.329"]]
|
||||
[cheshire "5.10.0"]]
|
||||
:deploy-repositories [["clojars" {:url "https://clojars.org/repo"
|
||||
:username :env/clojars_user
|
||||
:password :env/clojars_pass
|
||||
:username :env/babashka_nrepl_clojars_user
|
||||
:password :env/babashka_nrepl_clojars_pass
|
||||
:sign-releases false}]]
|
||||
:profiles {:test {:dependencies [[borkdude/sci "0.2.4"]]}})
|
||||
:profiles {:test {:dependencies [[borkdude/sci "0.0.13-alpha.27"]]}})
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env bb
|
||||
|
||||
(ns changelog
|
||||
(:require [clojure.string :as str]))
|
||||
|
||||
(let [changelog (slurp "CHANGELOG.md")
|
||||
replaced (str/replace changelog
|
||||
#" #(\d+)"
|
||||
(fn [[_ issue after]]
|
||||
(format " [#%s](https://github.com/babashka/pods/issues/%s)%s"
|
||||
issue issue (str after))))
|
||||
replaced (str/replace replaced
|
||||
#"@([a-zA-Z0-9-_]+)([, \.)])"
|
||||
(fn [[_ name after]]
|
||||
(format "[@%s](https://github.com/%s)%s"
|
||||
name name after)))]
|
||||
(spit "CHANGELOG.md" replaced))
|
||||
19
script/test
19
script/test
|
|
@ -1,32 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eou pipefail
|
||||
|
||||
export BABASHKA_POD_TEST_FORMAT
|
||||
export BABASHKA_POD_TEST_SOCKET
|
||||
|
||||
# format = edn
|
||||
BABASHKA_POD_TEST_FORMAT=edn
|
||||
echo "Testing edn"
|
||||
clojure -M:test -n babashka.pods.jvm-test
|
||||
clojure -M:sci:test -n babashka.pods.sci-test
|
||||
clojure -M:test -n babashka.pods.impl-test
|
||||
clojure -A:test -n babashka.pods.jvm-test
|
||||
clojure -A:sci:test -n babashka.pods.sci-test
|
||||
|
||||
# format = json
|
||||
BABASHKA_POD_TEST_FORMAT=json
|
||||
echo "Testing json"
|
||||
clojure -M:test -n babashka.pods.jvm-test
|
||||
clojure -M:sci:test -n babashka.pods.sci-test
|
||||
clojure -A:test -n babashka.pods.jvm-test
|
||||
clojure -A:sci:test -n babashka.pods.sci-test
|
||||
|
||||
# format = json
|
||||
BABASHKA_POD_TEST_FORMAT="transit+json"
|
||||
echo "Testing transit"
|
||||
clojure -M:test -n babashka.pods.jvm-test
|
||||
clojure -M:sci:test -n babashka.pods.sci-test
|
||||
clojure -A:test -n babashka.pods.jvm-test
|
||||
clojure -A:sci:test -n babashka.pods.sci-test
|
||||
|
||||
# socket = true
|
||||
unset BABASHKA_POD_TEST_FORMAT
|
||||
BABASHKA_POD_TEST_SOCKET=true
|
||||
echo "Testing socket"
|
||||
clojure -M:test -n babashka.pods.jvm-test
|
||||
clojure -M:sci:test -n babashka.pods.sci-test
|
||||
clojure -A:test -n babashka.pods.jvm-test
|
||||
clojure -A:sci:test -n babashka.pods.sci-test
|
||||
|
|
|
|||
|
|
@ -14,18 +14,3 @@
|
|||
(defn invoke
|
||||
([pod-id-or-pod sym args] (invoke pod-id-or-pod sym args {}))
|
||||
([pod-id-or-pod sym args opts] (jvm/invoke pod-id-or-pod sym args opts)))
|
||||
|
||||
(defmacro copy-var [name var]
|
||||
`(do (def ~name ~var)
|
||||
(let [m# (meta (var ~var))
|
||||
doc# (:doc m#)
|
||||
arglists# (:arglists m#)]
|
||||
(alter-meta! (var ~name) assoc
|
||||
:arglists arglists#
|
||||
:doc doc#))))
|
||||
|
||||
#_:clj-kondo/ignore
|
||||
(do
|
||||
(copy-var add-transit-read-handler! jvm/add-transit-read-handler!)
|
||||
(copy-var add-transit-write-handler! jvm/add-transit-write-handler!)
|
||||
(copy-var set-default-transit-write-handler! jvm/set-default-transit-write-handler!))
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@
|
|||
(defn bytes->string [^"[B" bytes]
|
||||
(String. bytes))
|
||||
|
||||
(defn bytes->boolean [^"[B" bytes]
|
||||
(= "true" (String. bytes)))
|
||||
|
||||
(defn get-string [m k]
|
||||
(-> (get m k)
|
||||
bytes->string))
|
||||
|
|
@ -39,63 +36,17 @@
|
|||
(some-> (get m k)
|
||||
bytes->string))
|
||||
|
||||
(defn get-maybe-boolean [m k]
|
||||
(some-> (get m k)
|
||||
bytes->boolean))
|
||||
|
||||
(defn next-id []
|
||||
(str (java.util.UUID/randomUUID)))
|
||||
|
||||
(def ^:dynamic *pod-id* nil)
|
||||
|
||||
(defonce transit-read-handlers (atom {}))
|
||||
(defonce transit-read-handler-maps (atom {}))
|
||||
|
||||
(defn update-transit-read-handler-map []
|
||||
(swap! transit-read-handler-maps assoc *pod-id*
|
||||
(transit/read-handler-map (get @transit-read-handlers *pod-id*))))
|
||||
|
||||
(defn transit-json-read [pod-id ^String s]
|
||||
(defn transit-json-read [^String s]
|
||||
(with-open [bais (java.io.ByteArrayInputStream. (.getBytes s "UTF-8"))]
|
||||
(let [r (transit/reader bais :json {:handlers (get @transit-read-handler-maps pod-id)})]
|
||||
(let [r (transit/reader bais :json)]
|
||||
(transit/read r))))
|
||||
|
||||
;; https://www.cognitect.com/blog/2015/9/10/extending-transit
|
||||
(defn add-transit-read-handler!
|
||||
([tag fn]
|
||||
(let [rh (transit/read-handler fn)]
|
||||
(swap! transit-read-handlers assoc-in [*pod-id* tag] rh)
|
||||
(update-transit-read-handler-map)
|
||||
nil)))
|
||||
|
||||
(defonce transit-write-handlers (atom {}))
|
||||
(defonce transit-write-handler-maps (atom {}))
|
||||
|
||||
(defn update-transit-write-handler-map []
|
||||
(swap! transit-write-handler-maps assoc *pod-id*
|
||||
(transit/write-handler-map (get @transit-write-handlers *pod-id*))))
|
||||
|
||||
;; https://www.cognitect.com/blog/2015/9/10/extending-transit
|
||||
(defn add-transit-write-handler!
|
||||
[classes tag fn]
|
||||
(let [rh (transit/write-handler tag fn)]
|
||||
(doseq [class classes]
|
||||
(swap! transit-write-handlers assoc-in [*pod-id* class] rh)))
|
||||
(update-transit-write-handler-map)
|
||||
nil)
|
||||
|
||||
(defonce transit-default-write-handlers (atom {}))
|
||||
|
||||
(defn set-default-transit-write-handler! [tag-fn val-fn]
|
||||
(let [wh (transit/write-handler tag-fn val-fn)]
|
||||
(swap! transit-default-write-handlers assoc *pod-id* wh)))
|
||||
|
||||
(defn transit-json-write
|
||||
[pod-id ^String s metadata?]
|
||||
(defn transit-json-write [^String s]
|
||||
(with-open [baos (java.io.ByteArrayOutputStream. 4096)]
|
||||
(let [w (transit/writer baos :json (cond-> {:handlers (get @transit-write-handler-maps pod-id)
|
||||
:default-handler (get @transit-default-write-handlers pod-id)}
|
||||
metadata? (assoc :transform transit/write-meta)))]
|
||||
(let [w (transit/writer baos :json)]
|
||||
(transit/write w s)
|
||||
(str baos))))
|
||||
|
||||
|
|
@ -107,7 +58,7 @@
|
|||
write-fn (case format
|
||||
:edn pr-str
|
||||
:json cheshire/generate-string
|
||||
:transit+json #(transit-json-write (:pod-id pod) % (:arg-meta opts)))
|
||||
:transit+json transit-json-write)
|
||||
id (next-id)
|
||||
chan (if handlers handlers
|
||||
(promise))
|
||||
|
|
@ -131,18 +82,11 @@
|
|||
#(Boolean/parseBoolean %))
|
||||
name-sym (symbol name)
|
||||
sym (symbol ns-name-str name)
|
||||
code (get-maybe-string var "code")
|
||||
vmeta (some-> (get var "meta")
|
||||
bytes->string
|
||||
edn/read-string)
|
||||
name-sym (if vmeta
|
||||
(with-meta name-sym vmeta)
|
||||
name-sym)
|
||||
metadata? (get-maybe-boolean var "arg-meta")]
|
||||
code (get-maybe-string var "code")]
|
||||
[name-sym
|
||||
(or code
|
||||
(fn [& args]
|
||||
(let [res (invoke pod sym args {:async async? :arg-meta metadata?})]
|
||||
(let [res (invoke pod sym args {:async async?})]
|
||||
res)))]))
|
||||
vars))
|
||||
|
||||
|
|
@ -168,96 +112,84 @@
|
|||
(throw e)))))
|
||||
:transit+json
|
||||
(fn [s]
|
||||
(try (transit-json-read (:pod-id pod) s)
|
||||
(try (transit-json-read s)
|
||||
(catch Exception e
|
||||
(binding [*out* *err*]
|
||||
(println "Cannot read Transit JSON: " (pr-str s))
|
||||
(throw e))))))]
|
||||
(binding [*pod-id* (:pod-id pod)]
|
||||
(try
|
||||
(loop []
|
||||
(let [reply (try (read stdout)
|
||||
(catch java.io.EOFException _
|
||||
::EOF)
|
||||
(catch java.net.SocketException e
|
||||
(if (= "Socket closed" (ex-message e))
|
||||
::EOF
|
||||
(throw e))))]
|
||||
(when-not (identical? ::EOF reply)
|
||||
(let [id (get reply "id")
|
||||
id (bytes->string id)
|
||||
value* (find reply "value")
|
||||
[exception value] (try (some->> value*
|
||||
second
|
||||
bytes->string
|
||||
read-fn
|
||||
(vector nil))
|
||||
(catch Exception e
|
||||
[e nil]))
|
||||
status (get reply "status")
|
||||
status (set (map (comp keyword bytes->string) status))
|
||||
error? (or exception (contains? status :error))
|
||||
done? (or error? exception (contains? status :done))
|
||||
[ex-message ex-data]
|
||||
(when error?
|
||||
[(or (some-> (get reply "ex-message")
|
||||
bytes->string)
|
||||
"")
|
||||
(or (some-> (get reply "ex-data")
|
||||
bytes->string
|
||||
read-fn)
|
||||
{})])
|
||||
namespace (when-let [v (get reply "vars")]
|
||||
(let [name-str (-> (get reply "name")
|
||||
bytes->string)
|
||||
name (symbol name-str)]
|
||||
{:name name
|
||||
:vars (bencode->vars pod name-str v)}))
|
||||
chan (get @chans id)
|
||||
promise? (instance? clojure.lang.IPending chan)
|
||||
exception (or exception
|
||||
(when (and promise? error?)
|
||||
(ex-info ex-message ex-data)))
|
||||
;; NOTE: if we need more fine-grained handlers, we will add
|
||||
;; a :raw handler that will just get the bencode message's raw
|
||||
;; data
|
||||
{error-handler :error
|
||||
done-handler :done
|
||||
success-handler :success} (when (map? chan)
|
||||
chan)
|
||||
out (some-> (get reply "out")
|
||||
bytes->string)
|
||||
err (some-> (get reply "err")
|
||||
bytes->string)]
|
||||
;; NOTE: write to out and err before delivering promise for making
|
||||
;; listening to output synchronous.
|
||||
(when out
|
||||
(binding [*out* out-stream]
|
||||
(print out)
|
||||
(.flush ^java.io.Writer out-stream)))
|
||||
(when err
|
||||
(binding [*out* err-stream]
|
||||
(print err)
|
||||
(.flush ^java.io.Writer err-stream)))
|
||||
(when (or value* error? namespace)
|
||||
(cond promise?
|
||||
(deliver chan (cond error? exception
|
||||
value value
|
||||
namespace namespace))
|
||||
(and (not error?) success-handler)
|
||||
(success-handler value)
|
||||
(and error? error-handler)
|
||||
(error-handler {:ex-message ex-message
|
||||
:ex-data ex-data})))
|
||||
(when (and done? (not error?))
|
||||
(when promise?
|
||||
(deliver chan nil))
|
||||
(when done-handler
|
||||
(done-handler))))
|
||||
(recur))))
|
||||
(catch Exception e
|
||||
(binding [*out* *err* #_err-stream]
|
||||
(prn e)))))))
|
||||
(try
|
||||
(loop []
|
||||
(let [reply (try (read stdout)
|
||||
(catch java.io.EOFException _
|
||||
::EOF))]
|
||||
(when-not (identical? ::EOF reply)
|
||||
(let [id (get reply "id")
|
||||
id (bytes->string id)
|
||||
value* (find reply "value")
|
||||
value (some-> value*
|
||||
second
|
||||
bytes->string
|
||||
read-fn)
|
||||
status (get reply "status")
|
||||
status (set (map (comp keyword bytes->string) status))
|
||||
error? (contains? status :error)
|
||||
done? (or error? (contains? status :done))
|
||||
[ex-message ex-data]
|
||||
(when error?
|
||||
[(or (some-> (get reply "ex-message")
|
||||
bytes->string)
|
||||
"")
|
||||
(or (some-> (get reply "ex-data")
|
||||
bytes->string
|
||||
read-fn)
|
||||
{})])
|
||||
namespace (when-let [v (get reply "vars")]
|
||||
(let [name-str (-> (get reply "name")
|
||||
bytes->string)
|
||||
name (symbol name-str)]
|
||||
{:name name
|
||||
:vars (bencode->vars pod name-str v)}))
|
||||
chan (get @chans id)
|
||||
promise? (instance? clojure.lang.IPending chan)
|
||||
exception (when (and promise? error?)
|
||||
(ex-info ex-message ex-data))
|
||||
;; NOTE: if we need more fine-grained handlers, we will add
|
||||
;; a :raw handler that will just get the bencode message's raw
|
||||
;; data
|
||||
{error-handler :error
|
||||
done-handler :done
|
||||
success-handler :success} (when (map? chan)
|
||||
chan)
|
||||
out (some-> (get reply "out")
|
||||
bytes->string)
|
||||
err (some-> (get reply "err")
|
||||
bytes->string)]
|
||||
;; NOTE: write to out and err before delivering promise for making
|
||||
;; listening to output synchronous.
|
||||
(when out
|
||||
(binding [*out* out-stream]
|
||||
(println out)))
|
||||
(when err (binding [*out* err-stream]
|
||||
(println err)))
|
||||
(when (or value* error? namespace)
|
||||
(cond promise?
|
||||
(deliver chan (cond error? exception
|
||||
value value
|
||||
namespace namespace))
|
||||
(and (not error?) success-handler)
|
||||
(success-handler value)
|
||||
(and error? error-handler)
|
||||
(error-handler {:ex-message ex-message
|
||||
:ex-data ex-data})))
|
||||
(when (and done? (not error?))
|
||||
(when promise?
|
||||
(deliver chan nil))
|
||||
(when done-handler
|
||||
(done-handler))))
|
||||
(recur))))
|
||||
(catch Exception e
|
||||
(binding [*out* *err* #_err-stream]
|
||||
(prn e))))))
|
||||
|
||||
(def pods (atom {}))
|
||||
|
||||
|
|
@ -269,24 +201,27 @@
|
|||
(defn lookup-pod [pod-id]
|
||||
(get @pods pod-id))
|
||||
|
||||
(defn destroy* [{:keys [:stdin :process :ops]}]
|
||||
(if (contains? ops :shutdown)
|
||||
(do (write stdin
|
||||
{"op" "shutdown"
|
||||
"id" (next-id)})
|
||||
(.waitFor ^Process process))
|
||||
(.destroy ^Process process)))
|
||||
|
||||
(defn destroy [pod-id-or-pod]
|
||||
(let [pod-id (get-pod-id pod-id-or-pod)]
|
||||
(when-let [pod (lookup-pod pod-id)]
|
||||
(destroy* pod)
|
||||
(if (contains? (:ops pod) :shutdown)
|
||||
(do (write (:stdin pod)
|
||||
{"op" "shutdown"
|
||||
"id" (next-id)})
|
||||
(.waitFor ^Process (:process pod)))
|
||||
(.destroy ^Process (:process pod)))
|
||||
(when-let [rns (:remove-ns pod)]
|
||||
(doseq [[ns-name _] (:namespaces pod)]
|
||||
(rns ns-name))))
|
||||
(swap! pods dissoc pod-id)
|
||||
nil))
|
||||
|
||||
(def next-pod-id
|
||||
(let [counter (atom 0)]
|
||||
(fn []
|
||||
(let [[o _] (swap-vals! counter inc)]
|
||||
o))))
|
||||
|
||||
(def bytes->symbol
|
||||
(comp symbol bytes->string))
|
||||
|
||||
|
|
@ -309,8 +244,7 @@
|
|||
the socket is connected."
|
||||
^Socket
|
||||
[^String hostname ^Integer port]
|
||||
(doto (Socket. hostname port)
|
||||
(.setTcpNoDelay true)))
|
||||
(Socket. hostname port))
|
||||
|
||||
(defn close-socket
|
||||
"Close the socket, and also closes its input and output streams."
|
||||
|
|
@ -320,8 +254,7 @@
|
|||
(catch java.net.SocketException _ nil)))
|
||||
|
||||
(defn port-file [pid]
|
||||
(doto (io/file (str ".babashka-pod-" pid ".port"))
|
||||
(.deleteOnExit)))
|
||||
(io/file (str ".babashka-pod-" pid ".port")))
|
||||
|
||||
(defn read-port [^java.io.File port-file]
|
||||
(loop []
|
||||
|
|
@ -330,104 +263,67 @@
|
|||
(let [s (slurp f)]
|
||||
(when (str/ends-with? s "\n")
|
||||
(str/trim s))))]
|
||||
(Integer/parseInt s)
|
||||
(Integer. s)
|
||||
(recur)))))
|
||||
|
||||
(defn debug [& strs]
|
||||
(binding [*out* *err*]
|
||||
(println (str/join " " (map pr-str strs)))))
|
||||
|
||||
(defn resolve-pod [pod-spec {:keys [:version :path :force] :as opts}]
|
||||
(when (qualified-symbol? pod-spec)
|
||||
(when (and (not version) (not path))
|
||||
(throw (IllegalArgumentException. "Version or path must be provided")))
|
||||
(when (and version path)
|
||||
(throw (IllegalArgumentException. "You must provide either version or path, not both"))))
|
||||
(let [resolved (when (and (qualified-symbol? pod-spec) version)
|
||||
(resolver/resolve pod-spec version force))
|
||||
opts (if resolved
|
||||
(if-let [extra-opts (:options resolved)]
|
||||
(merge opts extra-opts)
|
||||
opts)
|
||||
opts)
|
||||
pod-spec (cond
|
||||
resolved [(:executable resolved)]
|
||||
path [path]
|
||||
(string? pod-spec) [pod-spec]
|
||||
:else pod-spec)]
|
||||
{:pod-spec pod-spec, :opts opts}))
|
||||
|
||||
(defn run-pod [pod-spec {:keys [:transport] :as _opts}]
|
||||
(let [pb (ProcessBuilder. ^java.util.List pod-spec)
|
||||
socket? (identical? :socket transport)
|
||||
_ (if socket?
|
||||
(.inheritIO pb)
|
||||
(.redirectError pb java.lang.ProcessBuilder$Redirect/INHERIT))
|
||||
_ (cond-> (doto (.environment pb)
|
||||
(.put "BABASHKA_POD" "true"))
|
||||
socket? (.put "BABASHKA_POD_TRANSPORT" "socket"))
|
||||
p (.start pb)
|
||||
port-file (when socket? (port-file (.pid p)))
|
||||
socket-port (when socket? (read-port port-file))
|
||||
[socket stdin stdout]
|
||||
(if socket?
|
||||
(let [^Socket socket
|
||||
(loop []
|
||||
(if-let [sock (try (create-socket "localhost" socket-port)
|
||||
(catch java.net.ConnectException _
|
||||
nil))]
|
||||
sock
|
||||
(recur)))]
|
||||
[socket
|
||||
(.getOutputStream socket)
|
||||
(PushbackInputStream. (.getInputStream socket))])
|
||||
[nil (.getOutputStream p) (java.io.PushbackInputStream. (.getInputStream p))])]
|
||||
{:process p
|
||||
:socket socket
|
||||
:stdin stdin
|
||||
:stdout stdout}))
|
||||
|
||||
(defn describe-pod [{:keys [:stdin :stdout]}]
|
||||
(write stdin {"op" "describe"
|
||||
"id" (next-id)})
|
||||
(read stdout))
|
||||
|
||||
(defn describe->ops [describe-reply]
|
||||
(some->> (get describe-reply "ops") keys (map keyword) set))
|
||||
|
||||
(defn describe->metadata [describe-reply resolve-fn]
|
||||
(let [format (-> (get describe-reply "format") bytes->string keyword)
|
||||
ops (describe->ops describe-reply)
|
||||
readers (when (identical? :edn format)
|
||||
(read-readers describe-reply resolve-fn))]
|
||||
{:format format, :ops ops, :readers readers}))
|
||||
|
||||
(defn run-pod-for-metadata [pod-spec opts]
|
||||
(let [running-pod (run-pod pod-spec opts)
|
||||
describe-reply (describe-pod running-pod)
|
||||
ops (describe->ops describe-reply)]
|
||||
(destroy* (assoc running-pod :ops ops))
|
||||
describe-reply))
|
||||
|
||||
(defn load-pod-metadata [unresolved-pod-spec {:keys [:download-only] :as opts}]
|
||||
(let [{:keys [:pod-spec :opts]} (resolve-pod unresolved-pod-spec opts)]
|
||||
(if download-only
|
||||
(resolver/warn "Not running pod" unresolved-pod-spec "to pre-cache metadata because OS and/or arch are different than system")
|
||||
(run-pod-for-metadata pod-spec opts))))
|
||||
;; TODO: symbol -> look up pod in local cache, invoke if present, else
|
||||
;; download via package.
|
||||
;; What about versions?
|
||||
;; bb can package definitions of popular pods in its resources
|
||||
;; but what if the resources have an error - maybe best to fetch the definitions from github
|
||||
;; (load-pod 'org.babashka/postgresql)
|
||||
;; (load-pod 'org.babashka/postgresql_0.0.1)
|
||||
|
||||
(defn load-pod
|
||||
([pod-spec] (load-pod pod-spec nil))
|
||||
([pod-spec opts]
|
||||
(let [{:keys [:pod-spec :opts]} (resolve-pod pod-spec opts)
|
||||
{:keys [:remove-ns :resolve]} opts
|
||||
|
||||
{p :process, stdin :stdin, stdout :stdout, socket :socket
|
||||
:as running-pod}
|
||||
(run-pod pod-spec opts)
|
||||
|
||||
reply (or (:metadata opts)
|
||||
(describe-pod running-pod))
|
||||
{:keys [:format :ops :readers]} (describe->metadata reply resolve)
|
||||
(let [{:keys [:version :force]} opts
|
||||
resolved (when (qualified-symbol? pod-spec)
|
||||
(resolver/resolve pod-spec version force))
|
||||
opts (if resolved
|
||||
(if-let [extra-opts (:options resolved)]
|
||||
(merge opts extra-opts)
|
||||
opts)
|
||||
opts)
|
||||
{:keys [:remove-ns :resolve :transport]} opts
|
||||
pod-spec (cond resolved [(:executable resolved)]
|
||||
(string? pod-spec) [pod-spec]
|
||||
:else pod-spec)
|
||||
pb (ProcessBuilder. ^java.util.List pod-spec)
|
||||
socket? (identical? :socket transport)
|
||||
_ (if socket?
|
||||
(.inheritIO pb)
|
||||
(.redirectError pb java.lang.ProcessBuilder$Redirect/INHERIT))
|
||||
_ (cond-> (doto (.environment pb)
|
||||
(.put "BABASHKA_POD" "true"))
|
||||
socket? (.put "BABASHKA_POD_TRANSPORT" "socket"))
|
||||
p (.start pb)
|
||||
port-file (when socket? (port-file (.pid p)))
|
||||
socket-port (when socket? (read-port port-file))
|
||||
[socket stdin stdout]
|
||||
(if socket?
|
||||
(let [^Socket socket
|
||||
(loop []
|
||||
(if-let [sock (try (create-socket "localhost" socket-port)
|
||||
(catch java.net.ConnectException _
|
||||
nil))]
|
||||
sock
|
||||
(recur)))]
|
||||
[socket
|
||||
(.getOutputStream socket)
|
||||
(PushbackInputStream. (.getInputStream socket))])
|
||||
[nil (.getOutputStream p) (java.io.PushbackInputStream. (.getInputStream p))])
|
||||
_ (write stdin {"op" "describe"
|
||||
"id" (next-id)})
|
||||
reply (read stdout)
|
||||
format (-> (get reply "format") bytes->string keyword)
|
||||
ops (some->> (get reply "ops") keys (map keyword) set)
|
||||
readers (when (identical? :edn format)
|
||||
(read-readers reply resolve))
|
||||
pod {:process p
|
||||
:pod-spec pod-spec
|
||||
:stdin stdin
|
||||
|
|
|
|||
|
|
@ -15,43 +15,29 @@
|
|||
"x86_64"
|
||||
arch))
|
||||
|
||||
(defn normalize-os [os]
|
||||
(-> os str/lower-case (str/replace #"\s+" "_")))
|
||||
|
||||
(def os
|
||||
(delay
|
||||
{:os/name (or (System/getenv "BABASHKA_PODS_OS_NAME")
|
||||
(System/getProperty "os.name"))
|
||||
:os/arch (let [arch (or (System/getenv "BABASHKA_PODS_OS_ARCH")
|
||||
(System/getProperty "os.arch"))]
|
||||
(normalize-arch arch))}))
|
||||
|
||||
(def os {:os/name (System/getProperty "os.name")
|
||||
:os/arch (let [arch (System/getProperty "os.arch")]
|
||||
(normalize-arch arch))})
|
||||
(defn warn [& strs]
|
||||
(binding [*out* *err*]
|
||||
(apply println strs)))
|
||||
|
||||
(defn match-artifacts
|
||||
([package] (match-artifacts package (:os/arch @os)))
|
||||
([package arch]
|
||||
(let [artifacts (:pod/artifacts package)
|
||||
res (filter (fn [{os-name :os/name
|
||||
os-arch :os/arch}]
|
||||
(let [os-arch (normalize-arch os-arch)]
|
||||
(and (re-matches (re-pattern os-name) (:os/name @os))
|
||||
(re-matches (re-pattern os-arch)
|
||||
arch))))
|
||||
artifacts)]
|
||||
(if (empty? res)
|
||||
(if (and (= "Mac OS X" (:os/name @os))
|
||||
(= "aarch64" (:os/arch @os)))
|
||||
;; Rosetta2 fallback on Apple M1 machines
|
||||
(match-artifacts package "x86_64")
|
||||
(throw (IllegalArgumentException. (format "No executable found for pod %s (%s) and OS %s/%s"
|
||||
(:pod/name package)
|
||||
(:pod/version package)
|
||||
(:os/name @os)
|
||||
(:os/arch @os)))))
|
||||
res))))
|
||||
(defn match-artifacts [package]
|
||||
(let [artifacts (:pod/artifacts package)
|
||||
res (filter (fn [{os-name :os/name
|
||||
os-arch :os/arch}]
|
||||
(let [os-arch (normalize-arch os-arch)]
|
||||
(and (re-matches (re-pattern os-name) (:os/name os))
|
||||
(re-matches (re-pattern os-arch)
|
||||
(:os/arch os)))))
|
||||
artifacts)]
|
||||
(when (empty? res)
|
||||
(throw (IllegalArgumentException. (format "No executable found for pod %s (%s) and OS %s/%s"
|
||||
(:pod/name package)
|
||||
(:pod/version package)
|
||||
(:os/name os)
|
||||
(:os/arch os)))))
|
||||
res))
|
||||
|
||||
(defn unzip [{:keys [^java.io.File zip-file
|
||||
^java.io.File destination-dir
|
||||
|
|
@ -91,10 +77,7 @@
|
|||
^"[Ljava.nio.file.CopyOption;"
|
||||
(into-array
|
||||
[java.nio.file.StandardCopyOption/REPLACE_EXISTING])))
|
||||
(.mkdirs destination-dir)
|
||||
(let [res (sh "tar" "xf" (.getPath tmp-file) "--directory" (.getPath destination-dir))]
|
||||
(when-not (zero? (:exit res))
|
||||
(throw (ex-info (:err res) res))))
|
||||
(sh "tar" "xf" (.getPath tmp-file) "--directory" (.getPath destination-dir))
|
||||
(.delete tmp-file)))
|
||||
|
||||
(defn make-executable [dest-dir executables verbose?]
|
||||
|
|
@ -114,20 +97,11 @@
|
|||
(with-open [is (.getInputStream conn)]
|
||||
(io/copy is dest))))
|
||||
|
||||
(defn repo-dir []
|
||||
(io/file (if-let [pods-dir (System/getenv "BABASHKA_PODS_DIR")]
|
||||
(io/file pods-dir)
|
||||
(io/file (or
|
||||
(System/getenv "XDG_DATA_HOME")
|
||||
(System/getProperty "user.home"))
|
||||
".babashka"
|
||||
"pods"))
|
||||
"repository"))
|
||||
|
||||
(def pods-repo-dir
|
||||
(def pod-manifests-dir
|
||||
;; wrapped in delay for GraalVM native-image
|
||||
(delay
|
||||
(repo-dir)))
|
||||
(delay (io/file (or (System/getenv "XDG_DATA_HOME")
|
||||
(System/getProperty "user.home"))
|
||||
".babashka" "pods" "repository")))
|
||||
|
||||
(defn github-url [qsym version]
|
||||
(format
|
||||
|
|
@ -136,7 +110,7 @@
|
|||
|
||||
(defn pod-manifest
|
||||
[qsym version force?]
|
||||
(let [f (io/file @pods-repo-dir (str qsym) (str version) "manifest.edn")]
|
||||
(let [f (io/file @pod-manifests-dir (str qsym) (str version) "manifest.edn")]
|
||||
(if (and (not force?)
|
||||
(.exists f))
|
||||
(edn/read-string (slurp f))
|
||||
|
|
@ -147,30 +121,27 @@
|
|||
^java.io.File
|
||||
[{pod-name :pod/name
|
||||
pod-version :pod/version}]
|
||||
(let [base-file
|
||||
(if-let [pods-dir (System/getenv "BABASHKA_PODS_DIR")]
|
||||
(io/file pods-dir)
|
||||
(io/file (or
|
||||
(System/getenv "XDG_CACHE_HOME")
|
||||
(System/getProperty "user.home"))
|
||||
".babashka"
|
||||
"pods"))]
|
||||
(io/file base-file
|
||||
"repository"
|
||||
(str pod-name)
|
||||
pod-version
|
||||
(normalize-os (:os/name @os))
|
||||
(:os/arch @os))))
|
||||
(io/file (or
|
||||
(System/getenv "XDG_CACHE_HOME")
|
||||
(System/getProperty "user.home"))
|
||||
".babashka"
|
||||
"pods"
|
||||
"repository"
|
||||
(str pod-name)
|
||||
pod-version))
|
||||
|
||||
(defn data-dir
|
||||
^java.io.File
|
||||
[{pod-name :pod/name
|
||||
pod-version :pod/version}]
|
||||
(io/file @pods-repo-dir
|
||||
(io/file (or
|
||||
(System/getenv "XDG_DATA_HOME")
|
||||
(System/getProperty "user.home"))
|
||||
".babashka"
|
||||
"pods"
|
||||
"repository"
|
||||
(str pod-name)
|
||||
pod-version
|
||||
(normalize-os (:os/name @os))
|
||||
(:os/arch @os)))
|
||||
pod-version))
|
||||
|
||||
(defn sha256 [file]
|
||||
(let [buf (byte-array 8192)
|
||||
|
|
|
|||
|
|
@ -6,18 +6,17 @@
|
|||
(defn- unroot-resource [^String path]
|
||||
(symbol (.. path
|
||||
(substring 1)
|
||||
(replace \/ \.)
|
||||
(replace \/ \. )
|
||||
(replace \_ \-))))
|
||||
|
||||
(defn- process-namespace [{:keys [:name :vars]}]
|
||||
(binding [*ns* (load-string (format "(ns %s) *ns*" name))]
|
||||
(doseq [[var-sym v] vars]
|
||||
(when-let [maybe-core (some-> (ns-resolve *ns* var-sym) meta :ns str symbol)]
|
||||
(when (= 'clojure.core maybe-core)
|
||||
(ns-unmap *ns* var-sym)))
|
||||
(cond
|
||||
(ifn? v)
|
||||
(intern name var-sym v)
|
||||
(do
|
||||
(ns-unmap *ns* var-sym)
|
||||
(intern name var-sym v))
|
||||
(string? v)
|
||||
(load-string v)))))
|
||||
|
||||
|
|
@ -56,10 +55,9 @@
|
|||
(when defer?
|
||||
[ns-name pod]))
|
||||
namespaces)))
|
||||
(binding [impl/*pod-id* (:pod-id pod)]
|
||||
(doseq [[ns-sym vars lazy?] namespaces
|
||||
:when (not lazy?)]
|
||||
(process-namespace {:name ns-sym :vars vars})))
|
||||
(doseq [[ns-sym vars lazy?] namespaces
|
||||
:when (not lazy?)]
|
||||
(process-namespace {:name ns-sym :vars vars}))
|
||||
(future (impl/processor pod))
|
||||
{:pod/id (:pod-id pod)})))
|
||||
|
||||
|
|
@ -71,18 +69,3 @@
|
|||
(defn invoke
|
||||
([pod-id sym args] (invoke pod-id sym args {}))
|
||||
([pod-id sym args opts] (impl/invoke-public pod-id sym args opts)))
|
||||
|
||||
(defmacro copy-var [name var]
|
||||
`(do (def ~name ~var)
|
||||
(let [m# (meta (var ~var))
|
||||
doc# (:doc m#)
|
||||
arglists# (:arglists m#)]
|
||||
(alter-meta! (var ~name) assoc
|
||||
:arglists arglists#
|
||||
:doc doc#))))
|
||||
|
||||
#_:clj-kondo/ignore
|
||||
(do
|
||||
(copy-var add-transit-read-handler! impl/add-transit-read-handler!)
|
||||
(copy-var add-transit-write-handler! impl/add-transit-write-handler!)
|
||||
(copy-var set-default-transit-write-handler! impl/set-default-transit-write-handler!))
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
(ns babashka.pods.sci
|
||||
(:require [babashka.pods.impl :as impl]
|
||||
[sci.core :as sci]
|
||||
[clojure.java.io :as io]
|
||||
[babashka.pods.impl.resolver :as resolver]
|
||||
[babashka.fs :as fs])
|
||||
(:import (java.io PushbackInputStream File)))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
[sci.core :as sci]))
|
||||
|
||||
(defn- process-namespace [ctx {:keys [:name :vars]}]
|
||||
(let [env (:env ctx)
|
||||
|
|
@ -17,58 +11,14 @@
|
|||
(swap! env update-in [:namespaces ns-name]
|
||||
(fn [ns-map]
|
||||
(if ns-map ns-map {:obj sci-ns})))
|
||||
(doseq [[var-name var-value :as var] vars]
|
||||
(doseq [[var-name var-value] vars]
|
||||
(cond (ifn? var-value)
|
||||
(swap! env assoc-in [:namespaces ns-name var-name]
|
||||
(sci/new-var
|
||||
(symbol (str ns-name) (str var-name)) var-value (meta var-name)))
|
||||
(symbol (str ns-name) (str var-name)) var-value))
|
||||
(string? var-value)
|
||||
(sci/eval-string* ctx var-value))))))
|
||||
|
||||
(defn metadata-cache-file ^File [^File bb-edn-file pod-spec {:keys [:version :path]}]
|
||||
(if version
|
||||
(io/file (resolver/cache-dir {:pod/name pod-spec :pod/version version})
|
||||
"metadata.cache")
|
||||
(let [config-dir (.getParentFile bb-edn-file)
|
||||
cache-dir (io/file config-dir ".babashka")
|
||||
pod-file (-> path io/file .getName)
|
||||
cache-file (io/file cache-dir (str pod-file ".metadata.cache"))]
|
||||
cache-file)))
|
||||
|
||||
(defn load-metadata-from-cache [bb-edn-file pod-spec opts]
|
||||
(let [cache-file (metadata-cache-file bb-edn-file pod-spec opts)]
|
||||
(when (.exists cache-file)
|
||||
(with-open [r (PushbackInputStream. (io/input-stream cache-file))]
|
||||
(impl/read r)))))
|
||||
|
||||
(defn load-pod-metadata* [bb-edn-file pod-spec {:keys [:version :cache] :as opts}]
|
||||
(let [metadata (impl/load-pod-metadata pod-spec opts)
|
||||
cache-file (when (and metadata cache)
|
||||
(metadata-cache-file bb-edn-file pod-spec opts))]
|
||||
(when cache-file
|
||||
(io/make-parents cache-file)
|
||||
(when (fs/writable? (fs/parent cache-file))
|
||||
(with-open [w (io/output-stream cache-file)]
|
||||
(impl/write w metadata))))
|
||||
metadata))
|
||||
|
||||
(defn load-pod-metadata
|
||||
([pod-spec opts] (load-pod-metadata nil pod-spec opts))
|
||||
([bb-edn-file pod-spec {:keys [:cache] :as opts}]
|
||||
(let [metadata
|
||||
(if-let [cached-metadata (when cache
|
||||
(load-metadata-from-cache bb-edn-file
|
||||
pod-spec
|
||||
opts))]
|
||||
cached-metadata
|
||||
(load-pod-metadata* bb-edn-file pod-spec opts))]
|
||||
(reduce
|
||||
(fn [pod-namespaces ns]
|
||||
(let [ns-sym (-> ns (get "name") impl/bytes->string symbol)]
|
||||
(assoc pod-namespaces ns-sym {:pod-spec pod-spec
|
||||
:opts (assoc opts :metadata metadata)})))
|
||||
{} (get metadata "namespaces")))))
|
||||
|
||||
(defn load-pod
|
||||
([ctx pod-spec] (load-pod ctx pod-spec nil))
|
||||
([ctx pod-spec version opts] (load-pod ctx pod-spec (assoc opts :version version)))
|
||||
|
|
@ -115,10 +65,9 @@
|
|||
(when prev-load-fn
|
||||
(prev-load-fn m))))]
|
||||
(swap! env assoc :load-fn new-load-fn)))
|
||||
(binding [impl/*pod-id* (:pod-id pod)]
|
||||
(doseq [[ns-name vars lazy?] namespaces
|
||||
:when (not lazy?)]
|
||||
(process-namespace ctx {:name ns-name :vars vars})))
|
||||
(doseq [[ns-name vars lazy?] namespaces
|
||||
:when (not lazy?)]
|
||||
(process-namespace ctx {:name ns-name :vars vars}))
|
||||
(sci/future (impl/processor pod))
|
||||
{:pod/id (:pod-id pod)})))
|
||||
|
||||
|
|
@ -130,18 +79,3 @@
|
|||
(defn invoke
|
||||
([pod-id sym args] (invoke pod-id sym args {}))
|
||||
([pod-id sym args opts] (impl/invoke-public pod-id sym args opts)))
|
||||
|
||||
(defmacro copy-var [name var]
|
||||
`(do (def ~name ~var)
|
||||
(let [m# (meta (var ~var))
|
||||
doc# (:doc m#)
|
||||
arglists# (:arglists m#)]
|
||||
(alter-meta! (var ~name) assoc
|
||||
:arglists arglists#
|
||||
:doc doc#))))
|
||||
|
||||
#_:clj-kondo/ignore
|
||||
(do
|
||||
(copy-var add-transit-read-handler! impl/add-transit-read-handler!)
|
||||
(copy-var add-transit-write-handler! impl/add-transit-write-handler!)
|
||||
(copy-var set-default-transit-write-handler! impl/set-default-transit-write-handler!))
|
||||
|
|
|
|||
|
|
@ -36,34 +36,12 @@
|
|||
|
||||
(defn transit-json-read [^String s]
|
||||
(with-open [bais (java.io.ByteArrayInputStream. (.getBytes s "UTF-8"))]
|
||||
(let [r (transit/reader bais :json {:handlers
|
||||
{"local-date-time"
|
||||
(transit/read-handler
|
||||
(fn [s]
|
||||
(java.time.LocalDateTime/parse s)))
|
||||
"java.array"
|
||||
(transit/read-handler
|
||||
(fn [v]
|
||||
(into-array v)))}})]
|
||||
(let [r (transit/reader bais :json)]
|
||||
(transit/read r))))
|
||||
|
||||
(defn transit-json-write [s]
|
||||
(defn transit-json-write [^String s]
|
||||
(with-open [baos (java.io.ByteArrayOutputStream. 4096)]
|
||||
(let [w (transit/writer baos :json {:handlers
|
||||
{java.time.LocalDateTime
|
||||
(transit/write-handler
|
||||
"local-date-time"
|
||||
str)}
|
||||
:default-handler
|
||||
(transit/write-handler
|
||||
(fn [x] (when (.isArray (class x)) "java.array"))
|
||||
vec)})]
|
||||
(transit/write w s)
|
||||
(str baos))))
|
||||
|
||||
(defn transit-json-write-meta [s]
|
||||
(with-open [baos (java.io.ByteArrayOutputStream. 4096)]
|
||||
(let [w (transit/writer baos :json {:transform transit/write-meta})]
|
||||
(let [w (transit/writer baos :json)]
|
||||
(transit/write w s)
|
||||
(str baos))))
|
||||
|
||||
|
|
@ -116,8 +94,7 @@
|
|||
"my/other-tag" "pod.test-pod/read-other-tag"}
|
||||
"namespaces"
|
||||
[{"name" "pod.test-pod"
|
||||
"vars" (into [{"name" "add-sync"
|
||||
"meta" "{:doc \"add the arguments\"}"}
|
||||
"vars" (into [{"name" "add-sync"}
|
||||
{"name" "range-stream"
|
||||
"async" "true"}
|
||||
{"name" "assoc"}
|
||||
|
|
@ -134,35 +111,7 @@
|
|||
{"name" "other-tag"}
|
||||
;; reads thing with other tag
|
||||
{"name" "read-other-tag"
|
||||
"code" "(defn read-other-tag [x] [x x])"
|
||||
"meta" "{:doc \"unread\"}"}
|
||||
{"name" "round-trip-meta"
|
||||
"arg-meta" "true"}
|
||||
{"name" "dont-round-trip-meta"
|
||||
"arg-meta" "false"}
|
||||
{"name" "-local-date-time"}
|
||||
{"name" "transit-stuff"
|
||||
"code" "
|
||||
(babashka.pods/add-transit-read-handler! \"local-date-time\"
|
||||
(fn [s] (java.time.LocalDateTime/parse s)))
|
||||
|
||||
(babashka.pods/add-transit-write-handler! #{java.time.LocalDateTime}
|
||||
\"local-date-time\"
|
||||
str )
|
||||
|
||||
(defn local-date-time [x]
|
||||
(-local-date-time x))
|
||||
|
||||
;; serialize Java arrays as vectors with tag java.array
|
||||
(babashka.pods/set-default-transit-write-handler!
|
||||
(fn [x] (when (.isArray (class x)) \"java.array\"))
|
||||
vec)
|
||||
|
||||
(babashka.pods/add-transit-read-handler! \"java.array\"
|
||||
into-array)
|
||||
|
||||
"}
|
||||
{"name" "incorrect-edn"}]
|
||||
"code" "(defn read-other-tag [x] [x x])"}]
|
||||
dependents)}
|
||||
{"name" "pod.test-pod.loaded"
|
||||
"defer" "true"}
|
||||
|
|
@ -186,91 +135,65 @@
|
|||
pod.test-pod/add-sync
|
||||
(try (let [ret (apply + args)]
|
||||
(write out
|
||||
{"value" (write-fn ret)
|
||||
"id" id
|
||||
"status" ["done"]}))
|
||||
{"value" (write-fn ret)
|
||||
"id" id
|
||||
"status" ["done"]}))
|
||||
(catch Exception e
|
||||
(write out
|
||||
{"ex-data" (write-fn {:args args})
|
||||
"ex-message" (.getMessage e)
|
||||
"status" ["done" "error"]
|
||||
"id" id})))
|
||||
{"ex-data" (write-fn {:args args})
|
||||
"ex-message" (.getMessage e)
|
||||
"status" ["done" "error"]
|
||||
"id" id})))
|
||||
pod.test-pod/range-stream
|
||||
(let [rng (apply range args)]
|
||||
(doseq [v rng]
|
||||
(write out
|
||||
{"value" (write-fn v)
|
||||
"id" id})
|
||||
{"value" (write-fn v)
|
||||
"id" id})
|
||||
(Thread/sleep 100))
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id}))
|
||||
{"status" ["done"]
|
||||
"id" id}))
|
||||
pod.test-pod/assoc
|
||||
(write out
|
||||
{"value" (write-fn (apply assoc args))
|
||||
"status" ["done"]
|
||||
"id" id})
|
||||
{"value" (write-fn (apply assoc args))
|
||||
"status" ["done"]
|
||||
"id" id})
|
||||
pod.test-pod/error
|
||||
(write out
|
||||
{"ex-data" (write-fn {:args args})
|
||||
"ex-message" (str "Illegal arguments")
|
||||
"status" ["done" "error"]
|
||||
"id" id})
|
||||
{"ex-data" (write-fn {:args args})
|
||||
"ex-message" (str "Illegal arguments")
|
||||
"status" ["done" "error"]
|
||||
"id" id})
|
||||
pod.test-pod/print
|
||||
(do (write out
|
||||
{"out" (with-out-str (prn args))
|
||||
"id" id})
|
||||
{"out" (pr-str args)
|
||||
"id" id})
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id}))
|
||||
{"status" ["done"]
|
||||
"id" id}))
|
||||
pod.test-pod/print-err
|
||||
(do (write out
|
||||
{"err" (with-out-str (prn args))
|
||||
"id" id})
|
||||
{"err" (pr-str args)
|
||||
"id" id})
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id}))
|
||||
{"status" ["done"]
|
||||
"id" id}))
|
||||
pod.test-pod/return-nil
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value" (write-fn nil)})
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value" (write-fn nil)})
|
||||
pod.test-pod/reader-tag
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value" "#my/tag[1 2 3]"})
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value" "#my/tag[1 2 3]"})
|
||||
pod.test-pod/other-tag
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value" "#my/other-tag[1]"})
|
||||
pod.test-pod/round-trip-meta
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value"
|
||||
(case format
|
||||
:transit+json (transit-json-write-meta (first args))
|
||||
(write-fn (first args)))})
|
||||
pod.test-pod/dont-round-trip-meta
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value"
|
||||
(case format
|
||||
:transit+json (transit-json-write-meta (first args))
|
||||
(write-fn (first args)))})
|
||||
pod.test-pod/-local-date-time
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value" (write-fn (first args))})
|
||||
pod.test-pod/incorrect-edn
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value" (write-fn {(keyword "foo bar") 1})}))
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"value" "#my/other-tag[1]"}))
|
||||
(recur))
|
||||
:shutdown (System/exit 0)
|
||||
:load-ns (let [ns (-> (get message "ns")
|
||||
|
|
@ -281,20 +204,20 @@
|
|||
(case ns
|
||||
pod.test-pod.loaded
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"name" "pod.test-pod.loaded"
|
||||
"vars" [{"name" "loaded"
|
||||
"code" "(defn loaded [x] (inc x))"}]})
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"name" "pod.test-pod.loaded"
|
||||
"vars" [{"name" "loaded"
|
||||
"code" "(defn loaded [x] (inc x))"}]})
|
||||
pod.test-pod.loaded2
|
||||
(write out
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"name" "pod.test-pod.loaded2"
|
||||
"vars" [{"name" "x"
|
||||
"code" "(require '[pod.test-pod.loaded :as loaded])"}
|
||||
{"name" "loaded"
|
||||
"code" "(defn loaded [x] (loaded/loaded x))"}]}))
|
||||
{"status" ["done"]
|
||||
"id" id
|
||||
"name" "pod.test-pod.loaded2"
|
||||
"vars" [{"name" "x"
|
||||
"code" "(require '[pod.test-pod.loaded :as loaded])"}
|
||||
{"name" "loaded"
|
||||
"code" "(defn loaded [x] (loaded/loaded x))"}]}))
|
||||
(recur)))))))
|
||||
(catch Exception e
|
||||
(binding [*out* *err*]
|
||||
|
|
@ -302,6 +225,6 @@
|
|||
|
||||
(defn -main [& args]
|
||||
#_(binding [*out* *err*]
|
||||
(prn :args args))
|
||||
(prn :args args))
|
||||
(when (= "true" (System/getenv "BABASHKA_POD"))
|
||||
(run-pod (set args))))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(require '[babashka.pods :as pods])
|
||||
|
||||
(pods/load-pod 'org.babashka/buddy "0.1.0")
|
||||
(pods/load-pod 'org.babashka/buddy "0.0.1")
|
||||
|
||||
(require '[pod.babashka.buddy.codecs :as codecs]
|
||||
'[pod.babashka.buddy.hash :as hash])
|
||||
|
|
@ -8,4 +8,4 @@
|
|||
(println (-> (hash/sha256 "foobar")
|
||||
(codecs/bytes->hex)))
|
||||
|
||||
(pods/load-pod 'org.babashka/etaoin) ;; should cause error when version & path are missing
|
||||
(pods/load-pod 'org.babashka/etaoin) ;; should cause error when version is missing
|
||||
|
|
|
|||
|
|
@ -5,13 +5,9 @@
|
|||
|
||||
(def socket (System/getenv "BABASHKA_POD_TEST_SOCKET"))
|
||||
|
||||
(def cmd (cond-> ["clojure" "-M:test-pod"]
|
||||
(= "json" fmt) (conj "--json")
|
||||
(= "transit+json" fmt) (conj "--transit+json")))
|
||||
|
||||
;; (.println System/err cmd)
|
||||
|
||||
(def pod-id (:pod/id (pods/load-pod cmd
|
||||
(def pod-id (:pod/id (pods/load-pod (cond-> ["clojure" "-A:test-pod"]
|
||||
(= "json" fmt) (conj "--json")
|
||||
(= "transit+json" fmt) (conj "--transit+json"))
|
||||
{:socket (boolean socket)})))
|
||||
|
||||
(require '[pod.test-pod :as pod])
|
||||
|
|
@ -55,10 +51,6 @@
|
|||
(def add-result (pod.test-pod/add-sync 1 2 3))
|
||||
(def nil-result (pod.test-pod/return-nil))
|
||||
|
||||
(def add-sync-meta (:doc (meta #'pod.test-pod/add-sync)))
|
||||
(def error-meta (:doc (meta #'pod.test-pod/error)))
|
||||
(def read-other-tag-meta (:doc (meta #'pod.test-pod/read-other-tag)))
|
||||
|
||||
(def x9 pod.test-pod/x9)
|
||||
|
||||
(def tagged (if (= "edn" fmt)
|
||||
|
|
@ -72,43 +64,12 @@
|
|||
|
||||
(def fn-called (pod.test-pod/fn-call inc 2))
|
||||
|
||||
;; (.println System/err (str :fmt " " fmt))
|
||||
(def local-date-time
|
||||
(if (= "transit+json" fmt)
|
||||
(instance? java.time.LocalDateTime (pod.test-pod/local-date-time (java.time.LocalDateTime/now)))
|
||||
true))
|
||||
|
||||
(def assoc-string-array
|
||||
(if (= "transit+json" fmt)
|
||||
(let [v (:a (pod.test-pod/assoc {} :a (into-array String ["foo"])))]
|
||||
(.isArray (class v)))
|
||||
true))
|
||||
|
||||
(def round-trip-meta
|
||||
(if (= "transit+json" fmt)
|
||||
(= {:my-meta 2} (meta (pod.test-pod/round-trip-meta (with-meta [2] {:my-meta 2}))))
|
||||
true))
|
||||
|
||||
(def round-trip-meta-nested
|
||||
(if (= "transit+json" fmt)
|
||||
(= {:my-meta 3} (meta (first (pod.test-pod/round-trip-meta [(with-meta [3] {:my-meta 3})]))))
|
||||
true))
|
||||
|
||||
(def dont-round-trip-meta
|
||||
(if (= "transit+json" fmt)
|
||||
(= nil (meta (pod.test-pod/dont-round-trip-meta (with-meta [2] {:my-meta 2}))))
|
||||
true))
|
||||
|
||||
(require '[pod.test-pod.only-code :as only-code])
|
||||
(def should-be-1 (only-code/foo))
|
||||
|
||||
(require '[pod.test-pod.loaded2 :as loaded2])
|
||||
(def loaded (loaded2/loaded 1))
|
||||
|
||||
(def incorrect-edn-response
|
||||
(try (pod.test-pod/incorrect-edn)
|
||||
(catch Exception e (ex-message e))))
|
||||
|
||||
(pods/unload-pod pod-id)
|
||||
(def successfully-removed (nil? (find-ns 'pod.test-pod)))
|
||||
|
||||
|
|
@ -129,13 +90,4 @@
|
|||
other-tagged
|
||||
loaded
|
||||
fn-called
|
||||
local-date-time
|
||||
assoc-string-array
|
||||
round-trip-meta
|
||||
round-trip-meta-nested
|
||||
dont-round-trip-meta
|
||||
should-be-1
|
||||
add-sync-meta
|
||||
error-meta
|
||||
read-other-tag-meta
|
||||
incorrect-edn-response]
|
||||
should-be-1]
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
(ns babashka.pods.impl-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[babashka.pods.impl :refer :all]))
|
||||
|
||||
(deftest load-pod-test
|
||||
(testing "resolve fn gets called when pod has EDN data readers"
|
||||
(let [resolved? (atom false)
|
||||
test-resolve (fn [_sym]
|
||||
(reset! resolved? true))]
|
||||
(load-pod ["clojure" "-M:test-pod"] {:resolve test-resolve})
|
||||
(is @resolved?))))
|
||||
|
|
@ -19,10 +19,10 @@
|
|||
(let [out (java.io.StringWriter.)
|
||||
err (java.io.StringWriter.)
|
||||
ex (binding [*out* out
|
||||
*err* err]
|
||||
*err* err]
|
||||
(try (load-string
|
||||
pod-registry)
|
||||
(catch Exception e
|
||||
e)))]
|
||||
(is (str/includes? (str out) "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"))
|
||||
(is (str/includes? (pr-str ex) "Version or path must be provided"))))
|
||||
(is (str/includes? (pr-str ex) "Version must be provided" ))))
|
||||
|
|
|
|||
|
|
@ -13,19 +13,12 @@
|
|||
{'load-pod (fn [& args]
|
||||
(apply pods/load-pod @ctx-ref args))
|
||||
'invoke pods/invoke
|
||||
'unload-pod pods/unload-pod
|
||||
'add-transit-read-handler! pods/add-transit-read-handler!
|
||||
'add-transit-write-handler! pods/add-transit-write-handler!
|
||||
'set-default-transit-write-handler! pods/set-default-transit-write-handler!}}
|
||||
:classes {'System System
|
||||
'java.time.LocalDateTime java.time.LocalDateTime
|
||||
'java.lang.Class Class}})
|
||||
'unload-pod pods/unload-pod}}
|
||||
:classes {'System System}})
|
||||
_ (vreset! ctx-ref ctx)
|
||||
ret (sci/binding [sci/out out
|
||||
sci/err err]
|
||||
(binding [*out* out
|
||||
*err* err]
|
||||
(sci/eval-string* ctx test-program)))]
|
||||
(sci/eval-string* ctx test-program))]
|
||||
(assertions out err ret)))
|
||||
|
||||
(deftest pod-registry-test
|
||||
|
|
@ -38,4 +31,4 @@
|
|||
(catch Exception e
|
||||
e)))]
|
||||
(is (str/includes? (str out) "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"))
|
||||
(is (str/includes? (pr-str ex) "Version or path must be provided"))))
|
||||
(is (str/includes? (pr-str ex) "Version must be provided" ))))
|
||||
|
|
|
|||
|
|
@ -1,54 +1,34 @@
|
|||
(ns babashka.pods.test-common
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
[clojure.test :refer [is]]))
|
||||
|
||||
(def test-program (slurp (io/file "test-resources" "test_program.clj")))
|
||||
|
||||
(defn assertions [out err ret]
|
||||
;; (.println System/err ret)
|
||||
;; (.println System/err out)
|
||||
;; (.println System/err err)
|
||||
(doseq [[expected actual]
|
||||
(map vector (replace
|
||||
{::edn-error (if (= "edn"
|
||||
(System/getenv "BABASHKA_POD_TEST_FORMAT"))
|
||||
"Map literal must contain an even number of forms"
|
||||
::dont-care)}
|
||||
'["pod.test-pod"
|
||||
pod.test-pod
|
||||
{:a 1, :b 2}
|
||||
6
|
||||
3
|
||||
[1 2 3 4 5 6 7 8 9]
|
||||
#"Illegal arguments / \{:args [\(\[]1 2 3[\)\]]\}"
|
||||
nil
|
||||
3
|
||||
#"cast"
|
||||
{:args ["1" 2]}
|
||||
true
|
||||
9
|
||||
[1 2 3]
|
||||
[[1] [1]]
|
||||
2
|
||||
3
|
||||
true ;; local-date
|
||||
true ;; roundtrip string array
|
||||
true ;; roundtrip metadata
|
||||
true ;; roundtrip metadata nested
|
||||
true ;; dont roundtrip metadata (when arg-meta "false"/ absent)
|
||||
1
|
||||
"add the arguments"
|
||||
nil
|
||||
nil
|
||||
::edn-error])
|
||||
(map vector '["pod.test-pod"
|
||||
pod.test-pod
|
||||
{:a 1, :b 2}
|
||||
6
|
||||
3
|
||||
[1 2 3 4 5 6 7 8 9]
|
||||
#"Illegal arguments / \{:args [\(\[]1 2 3[\)\]]\}"
|
||||
nil
|
||||
3
|
||||
#"cast"
|
||||
{:args ["1" 2]}
|
||||
true
|
||||
9
|
||||
[1 2 3]
|
||||
[[1] [1]]
|
||||
2
|
||||
3
|
||||
1]
|
||||
(concat ret (repeat ::nil)))]
|
||||
(cond (instance? java.util.regex.Pattern expected)
|
||||
(is (re-find expected actual))
|
||||
(= ::dont-care expected) nil
|
||||
:else
|
||||
(is (= expected actual))))
|
||||
(if (instance? java.util.regex.Pattern expected)
|
||||
(is (re-find expected actual))
|
||||
(is (= expected actual))))
|
||||
(is (= "(\"hello\" \"print\" \"this\" \"debugging\" \"message\")\n:foo\n:foo\n" (str out)))
|
||||
(is (str/starts-with? (str err) "(\"hello\" \"print\" \"this\" \"error\")" )))
|
||||
(is (= "(\"hello\" \"print\" \"this\" \"error\")\n" (str err))))
|
||||
|
||||
(def pod-registry (slurp (io/file "test-resources" "pod_registry.clj")))
|
||||
|
|
|
|||
Loading…
Reference in a new issue