Compare commits

...

15 commits

Author SHA1 Message Date
Ingy döt Net
47e55fe5e7
Fix typo (#71) 2024-05-08 14:17:43 +02:00
Michiel Borkent
717cef7af5 ignore 2024-02-27 11:52:37 +01:00
Jo Geraerts
cd968459a7
correct small typo in README.md (#69) 2024-01-26 13:26:09 +01:00
Flávio Sousa
8b717eb001
Update README.md with BABASHKA_PODS_DIR (#68) 2023-06-05 11:13:35 +02:00
Michiel Borkent
6ad6045b94 minor 2023-05-12 16:42:25 +02:00
Jude Payne
b00133ca05
Fix #66: opt-in metadata (#67) 2023-05-12 16:09:40 +02:00
Michiel Borkent
64ecb94de8 changelog 2023-04-02 11:27:07 +02:00
Michiel Borkent
d29cf6aa65 Changelog 2023-04-02 11:26:24 +02:00
Michiel Borkent
1635931483 Fix #65, fix warnings when defining var with core name 2023-04-02 11:25:51 +02:00
Michiel Borkent
75c2216649 Changelog 2023-01-09 21:21:45 +01:00
Michiel Borkent
c2e3d8f8b8 Fix #59: delete port file on exit 2023-01-09 21:21:34 +01:00
Michiel Borkent
16bea5b7db Undo verbose 2023-01-09 21:19:04 +01:00
Michiel Borkent
85c554e643 Fix #63: create directory before un-tarring 2023-01-09 21:16:53 +01:00
Michiel Borkent
4fb0da7daf changelog script 2022-12-29 17:10:04 +01:00
Michiel Borkent
76313a7089 changelog script 2022-12-29 17:07:51 +01:00
10 changed files with 189 additions and 68 deletions

4
.gitignore vendored
View file

@ -3,3 +3,7 @@
.lein-failures .lein-failures
/pom.xml /pom.xml
.lein-repl-history .lein-repl-history
.cache
.clj-kondo/babashka
.clj-kondo/rewrite-clj
src/scratch.clj

View file

@ -1,13 +1,20 @@
# Changelog # 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 ## v0.2.0
- #61: add transit as explicit JVM dependency - [#61](https://github.com/babashka/pods/issues/61): add transit as explicit JVM dependency
- #60: transform pod reader error into exception of caller - [#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) - Switch "out" and "err" messages to print and flush instead of `println` ([@justone](https://github.com/justone))
- Set TCP_NODELAY on transport socket (@retrogradeorbit) - Set TCP_NODELAY on transport socket ([@retrogradeorbit](https://github.com/retrogradeorbit))
- Allow env vars OS_NAME & OS_ARCH to override os props (@cap10morgan) - Allow env vars OS_NAME & OS_ARCH to override os props ([@cap10morgan](https://github.com/cap10morgan))
- #49: don't log socket closed exception - [#49](https://github.com/babashka/pods/issues/49): don't log socket closed exception
## v0.1.0 ## v0.1.0

View file

@ -77,7 +77,7 @@ On the JVM:
When calling `load-pod` with a string or vector of strings (or declaring it in your `bb.edn`), 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). 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")` 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). 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 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 pods for a different OS / arch (e.g. for deployment to servers), you can set one or both of the following
@ -130,7 +130,7 @@ light weight replacement for native interop (JNI, JNA, etc.).
### Examples ### Examples
Beyond the already available pods mentioned above, eductional examples of pods Beyond the already available pods mentioned above, educational examples of pods
can be found [here](examples): can be found [here](examples):
- [pod-lispyclouds-sqlite](examples/pod-lispyclouds-sqlite): a pod that - [pod-lispyclouds-sqlite](examples/pod-lispyclouds-sqlite): a pod that
@ -228,7 +228,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"`. 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 stdoud. The pod client The pod encodes the above map to bencode and writes it to stdout. The pod client
reads this message from the pod's stdout. reads this message from the pod's stdout.
Upon receiving this message, the pod client creates these namespaces and vars. Upon receiving this message, the pod client creates these namespaces and vars.
@ -376,11 +376,15 @@ nil
#### Metadata #### 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 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 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 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 ignored if the var refers to Client-side code, since metadata can already be defined
in those code blocks. in those code blocks (see 'Dynamic Metadata' below to enable the encoding of metadata).
For example, a pod can define a function called `add`: For example, a pod can define a function called `add`:
@ -392,6 +396,33 @@ For example, a pod can define a function called `add`:
"meta" "{:doc \"arithmetic addition of 2 arguments\" :arglists ([a b])}"}]}]} "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 #### Deferred namespace loading
When your pod exposes multiple namespaces that can be used independently from When your pod exposes multiple namespaces that can be used independently from

17
script/changelog.clj Executable file
View file

@ -0,0 +1,17 @@
#!/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))

View file

@ -28,6 +28,9 @@
(defn bytes->string [^"[B" bytes] (defn bytes->string [^"[B" bytes]
(String. bytes)) (String. bytes))
(defn bytes->boolean [^"[B" bytes]
(= "true" (String. bytes)))
(defn get-string [m k] (defn get-string [m k]
(-> (get m k) (-> (get m k)
bytes->string)) bytes->string))
@ -36,6 +39,10 @@
(some-> (get m k) (some-> (get m k)
bytes->string)) bytes->string))
(defn get-maybe-boolean [m k]
(some-> (get m k)
bytes->boolean))
(defn next-id [] (defn next-id []
(str (java.util.UUID/randomUUID))) (str (java.util.UUID/randomUUID)))
@ -83,10 +90,12 @@
(let [wh (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))) (swap! transit-default-write-handlers assoc *pod-id* wh)))
(defn transit-json-write [pod-id ^String s] (defn transit-json-write
[pod-id ^String s metadata?]
(with-open [baos (java.io.ByteArrayOutputStream. 4096)] (with-open [baos (java.io.ByteArrayOutputStream. 4096)]
(let [w (transit/writer baos :json {:handlers (get @transit-write-handler-maps pod-id) (let [w (transit/writer baos :json (cond-> {:handlers (get @transit-write-handler-maps pod-id)
:default-handler (get @transit-default-write-handlers pod-id)})] :default-handler (get @transit-default-write-handlers pod-id)}
metadata? (assoc :transform transit/write-meta)))]
(transit/write w s) (transit/write w s)
(str baos)))) (str baos))))
@ -98,7 +107,7 @@
write-fn (case format write-fn (case format
:edn pr-str :edn pr-str
:json cheshire/generate-string :json cheshire/generate-string
:transit+json #(transit-json-write (:pod-id pod) %)) :transit+json #(transit-json-write (:pod-id pod) % (:arg-meta opts)))
id (next-id) id (next-id)
chan (if handlers handlers chan (if handlers handlers
(promise)) (promise))
@ -128,11 +137,12 @@
edn/read-string) edn/read-string)
name-sym (if vmeta name-sym (if vmeta
(with-meta name-sym vmeta) (with-meta name-sym vmeta)
name-sym)] name-sym)
metadata? (get-maybe-boolean var "arg-meta")]
[name-sym [name-sym
(or code (or code
(fn [& args] (fn [& args]
(let [res (invoke pod sym args {:async async?})] (let [res (invoke pod sym args {:async async? :arg-meta metadata?})]
res)))])) res)))]))
vars)) vars))
@ -310,7 +320,8 @@
(catch java.net.SocketException _ nil))) (catch java.net.SocketException _ nil)))
(defn port-file [pid] (defn port-file [pid]
(io/file (str ".babashka-pod-" pid ".port"))) (doto (io/file (str ".babashka-pod-" pid ".port"))
(.deleteOnExit)))
(defn read-port [^java.io.File port-file] (defn read-port [^java.io.File port-file]
(loop [] (loop []

View file

@ -91,7 +91,10 @@
^"[Ljava.nio.file.CopyOption;" ^"[Ljava.nio.file.CopyOption;"
(into-array (into-array
[java.nio.file.StandardCopyOption/REPLACE_EXISTING]))) [java.nio.file.StandardCopyOption/REPLACE_EXISTING])))
(sh "tar" "xf" (.getPath tmp-file) "--directory" (.getPath destination-dir)) (.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))))
(.delete tmp-file))) (.delete tmp-file)))
(defn make-executable [dest-dir executables verbose?] (defn make-executable [dest-dir executables verbose?]

View file

@ -12,11 +12,12 @@
(defn- process-namespace [{:keys [:name :vars]}] (defn- process-namespace [{:keys [:name :vars]}]
(binding [*ns* (load-string (format "(ns %s) *ns*" name))] (binding [*ns* (load-string (format "(ns %s) *ns*" name))]
(doseq [[var-sym v] vars] (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 (cond
(ifn? v) (ifn? v)
(do (intern name var-sym v)
(ns-unmap *ns* var-sym)
(intern name var-sym v))
(string? v) (string? v)
(load-string v))))) (load-string v)))))

View file

@ -61,6 +61,12 @@
(transit/write w s) (transit/write w s)
(str baos)))) (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})]
(transit/write w s)
(str baos))))
(defn run-pod [cli-args] (defn run-pod [cli-args]
(let [format (cond (contains? cli-args "--json") :json (let [format (cond (contains? cli-args "--json") :json
(contains? cli-args "--transit+json") :transit+json (contains? cli-args "--transit+json") :transit+json
@ -130,6 +136,10 @@
{"name" "read-other-tag" {"name" "read-other-tag"
"code" "(defn read-other-tag [x] [x x])" "code" "(defn read-other-tag [x] [x x])"
"meta" "{:doc \"unread\"}"} "meta" "{:doc \"unread\"}"}
{"name" "round-trip-meta"
"arg-meta" "true"}
{"name" "dont-round-trip-meta"
"arg-meta" "false"}
{"name" "-local-date-time"} {"name" "-local-date-time"}
{"name" "transit-stuff" {"name" "transit-stuff"
"code" " "code" "
@ -176,65 +186,81 @@
pod.test-pod/add-sync pod.test-pod/add-sync
(try (let [ret (apply + args)] (try (let [ret (apply + args)]
(write out (write out
{"value" (write-fn ret) {"value" (write-fn ret)
"id" id "id" id
"status" ["done"]})) "status" ["done"]}))
(catch Exception e (catch Exception e
(write out (write out
{"ex-data" (write-fn {:args args}) {"ex-data" (write-fn {:args args})
"ex-message" (.getMessage e) "ex-message" (.getMessage e)
"status" ["done" "error"] "status" ["done" "error"]
"id" id}))) "id" id})))
pod.test-pod/range-stream pod.test-pod/range-stream
(let [rng (apply range args)] (let [rng (apply range args)]
(doseq [v rng] (doseq [v rng]
(write out (write out
{"value" (write-fn v) {"value" (write-fn v)
"id" id}) "id" id})
(Thread/sleep 100)) (Thread/sleep 100))
(write out (write out
{"status" ["done"] {"status" ["done"]
"id" id})) "id" id}))
pod.test-pod/assoc pod.test-pod/assoc
(write out (write out
{"value" (write-fn (apply assoc args)) {"value" (write-fn (apply assoc args))
"status" ["done"] "status" ["done"]
"id" id}) "id" id})
pod.test-pod/error pod.test-pod/error
(write out (write out
{"ex-data" (write-fn {:args args}) {"ex-data" (write-fn {:args args})
"ex-message" (str "Illegal arguments") "ex-message" (str "Illegal arguments")
"status" ["done" "error"] "status" ["done" "error"]
"id" id}) "id" id})
pod.test-pod/print pod.test-pod/print
(do (write out (do (write out
{"out" (with-out-str (prn args)) {"out" (with-out-str (prn args))
"id" id}) "id" id})
(write out (write out
{"status" ["done"] {"status" ["done"]
"id" id})) "id" id}))
pod.test-pod/print-err pod.test-pod/print-err
(do (write out (do (write out
{"err" (with-out-str (prn args)) {"err" (with-out-str (prn args))
"id" id}) "id" id})
(write out (write out
{"status" ["done"] {"status" ["done"]
"id" id})) "id" id}))
pod.test-pod/return-nil pod.test-pod/return-nil
(write out (write out
{"status" ["done"] {"status" ["done"]
"id" id "id" id
"value" (write-fn nil)}) "value" (write-fn nil)})
pod.test-pod/reader-tag pod.test-pod/reader-tag
(write out (write out
{"status" ["done"] {"status" ["done"]
"id" id "id" id
"value" "#my/tag[1 2 3]"}) "value" "#my/tag[1 2 3]"})
pod.test-pod/other-tag pod.test-pod/other-tag
(write out (write out
{"status" ["done"] {"status" ["done"]
"id" id "id" id
"value" "#my/other-tag[1]"}) "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 pod.test-pod/-local-date-time
(write out (write out
{"status" ["done"] {"status" ["done"]
@ -255,20 +281,20 @@
(case ns (case ns
pod.test-pod.loaded pod.test-pod.loaded
(write out (write out
{"status" ["done"] {"status" ["done"]
"id" id "id" id
"name" "pod.test-pod.loaded" "name" "pod.test-pod.loaded"
"vars" [{"name" "loaded" "vars" [{"name" "loaded"
"code" "(defn loaded [x] (inc x))"}]}) "code" "(defn loaded [x] (inc x))"}]})
pod.test-pod.loaded2 pod.test-pod.loaded2
(write out (write out
{"status" ["done"] {"status" ["done"]
"id" id "id" id
"name" "pod.test-pod.loaded2" "name" "pod.test-pod.loaded2"
"vars" [{"name" "x" "vars" [{"name" "x"
"code" "(require '[pod.test-pod.loaded :as loaded])"} "code" "(require '[pod.test-pod.loaded :as loaded])"}
{"name" "loaded" {"name" "loaded"
"code" "(defn loaded [x] (loaded/loaded x))"}]})) "code" "(defn loaded [x] (loaded/loaded x))"}]}))
(recur))))))) (recur)))))))
(catch Exception e (catch Exception e
(binding [*out* *err*] (binding [*out* *err*]

View file

@ -84,6 +84,21 @@
(.isArray (class v))) (.isArray (class v)))
true)) 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]) (require '[pod.test-pod.only-code :as only-code])
(def should-be-1 (only-code/foo)) (def should-be-1 (only-code/foo))
@ -116,6 +131,9 @@
fn-called fn-called
local-date-time local-date-time
assoc-string-array assoc-string-array
round-trip-meta
round-trip-meta-nested
dont-round-trip-meta
should-be-1 should-be-1
add-sync-meta add-sync-meta
error-meta error-meta

View file

@ -34,6 +34,9 @@
3 3
true ;; local-date true ;; local-date
true ;; roundtrip string array true ;; roundtrip string array
true ;; roundtrip metadata
true ;; roundtrip metadata nested
true ;; dont roundtrip metadata (when arg-meta "false"/ absent)
1 1
"add the arguments" "add the arguments"
nil nil