babashka

https://github.com/babashka/babashka. Also see #sci, #nbb and #babashka-circleci-builds .
jumar 2020-10-13T07:44:38.379700Z

I'm trying to parse a json message from aws cli but got stuck pretty early - what am I doing wrong?

echo '{"DecodedMessage" : "{\"allowed\":false,\"explicitDeny\":false}"}' | bb -i "(do (require '[cheshire.core :as json]) (let [{:strs [DecodedMessage] :as msg} (json/parse-string *input*)] (println DecodedMessage) ))" | bb -i "(do (require '[cheshire.core :as json])
          (let [{:strs [DecodedMessage] :as msg} (json/parse-string *input*)] (println DecodedMessage) ))"
----- Error --------------------------------------------------------------------
Type:     java.lang.ClassCastException
Message:  clojure.lang.Cons cannot be cast to java.lang.String
Location: <expr>:2:50

----- Context ------------------------------------------------------------------
1: (do (require '[cheshire.core :as json])
2:           (let [{:strs [DecodedMessage] :as msg} (json/parse-string *input*)] (println DecodedMessage) ))
                                                    ^--- clojure.lang.Cons cannot be cast to java.lang.String

----- Stack trace --------------------------------------------------------------
cheshire.core/parse-string - <built-in>
user                       - <expr>:2:50
In the REPL, this works:
(def *input* "{\"DecodedMessage\" : \"{\\\"allowed\\\":false,\\\"explicitDeny\\\":false}\"}")
(do (require '[cheshire.core :as json])
          (let [{:strs [DecodedMessage] :as msg} (json/parse-string *input*)] (println DecodedMessage) ))
;;=> 
{"allowed":false,"explicitDeny":false}

2020-10-13T07:50:12.379800Z

I think

-i                  Bind *input* to a lazy seq of lines from stdi
@jumar Can you try replacing -i with -e?

jumar 2020-10-13T07:51:53.380Z

Ah right, now I'm getting a different error πŸ™‚

----- Error --------------------------------------------------------------------
Type:     clojure.lang.EdnReader$ReaderException
Message:  java.lang.RuntimeException: Invalid token: :
Location: 1:99

----- Stack trace --------------------------------------------------------------
                           - <expr>:1:99
cheshire.core/parse-string - <built-in>
user                       - <expr>:1:80

jumar 2020-10-13T07:52:19.380200Z

Btw. is this how would you write such a simple thing? Is that require even "required"?

borkdude 2020-10-13T07:56:11.380800Z

You can just use cheshire for this with parse-stream and *in*

borkdude 2020-10-13T07:57:03.381Z

When using -i then input becomes a lazy seq of lines. You should be aware of this. Cheshire cannot parse a lazy seq of text, it just parses text.

jumar 2020-10-13T07:59:42.381900Z

Well, I replaced -i with -e as per your suggestion. I'll try parse-stream

jumar 2020-10-13T08:00:03.382100Z

That works!

jumar 2020-10-13T08:00:10.382300Z

echo '{"DecodedMessage" : "{\"allowed\":false,\"explicitDeny\":false}"}' | bb -e "(do (require '[cheshire.core :as json]) (let [{:strs [DecodedMessage] :as msg} (json/parse-stream *in*)] (println DecodedMessage) ))"
{"allowed":false,"explicitDeny":false}

2020-10-13T08:56:59.382500Z

@borkdude I think forgetting -i or using *input* instead of *in* are easy mistakes to make. Did you consider using something like delays to gives some user feedback here? E.g. @`*input*` could throw an error if -i is not used. Maybe it would also be helpful if there was one var for a particular input. Now *input* can have different shapes based on the flag. I understand something like this would be a breaking change, but I’m curious what you think

borkdude 2020-10-13T09:00:31.382700Z

You don't need to use -i and *input* together, if you don't provide -i input contains EDN from stdin

borkdude 2020-10-13T09:01:21.382900Z

Just read the docs, it's explained pretty elaborately.

πŸ‘ 1
borkdude 2020-10-13T09:01:54.383100Z

And it's optional to use, you can also just use *in* and write more verbose code.

2020-10-13T09:31:34.383300Z

Yes I understand that reading the docs would help. I was thinking there might be a more foolproof way. But your position is clear :)

borkdude 2020-10-13T09:32:46.383600Z

These flags were invented when babashka was more or less just for one-liners and it could not run scripts, namespaces, macros, etc, etc. I don't use these flags a lot myself. Maybe I should hide them a little bit in the README ;)

πŸ‘ 1
2020-10-13T09:33:28.383800Z

maybe hide them behind a flag --show-flags πŸ˜…

borkdude 2020-10-13T09:34:18.384300Z

I was also thinking we could have another flag -j for JSON ;)

borkdude 2020-10-13T09:34:36.384500Z

or maybe --input json

borkdude 2020-10-13T09:35:17.384700Z

--input lines, --input edn, --input edn-stream, --input json, --input json-stream

2020-10-13T09:36:49.384900Z

It sounds useful. I haven’t done much with input yet. The potential error cases would grow I’m guessing, maybe like the example above

borkdude 2020-10-13T09:40:04.385100Z

maybe --input lines and --input edn-stream is more clear than -i and -I

borkdude 2020-10-13T09:40:21.385300Z

it defaults to --input edn (only one edn val from stdin)

borkdude 2020-10-13T09:41:14.385500Z

also --output lines and --output edn-stream

borkdude 2020-10-13T09:42:28.385700Z

https://github.com/borkdude/babashka/issues/613

2020-10-13T09:42:45.386100Z

Or an idea could be to not have flags, but leave it to the program? Maybe something like https://github.com/originrose/lazy-map that you would call like (:edn-stream *input*) . It would start parsing on the first call (i.e. lazy). If the value is incorrect it would throw. If there is no input it would throw. Would this make sense?

borkdude 2020-10-13T09:44:24.386800Z

I think those would just be functions based on *in*

borkdude 2020-10-13T09:44:34.387Z

no special libs needed. can already write those

πŸ‘ 1
2020-10-13T09:45:41.387200Z

That’s true, only benefit would be that you don’t have to have a reference somewhere. But maybe it’s too magical for little benefit

borkdude 2020-10-13T09:46:25.387500Z

The benefit of not having flags is that it works better for scripts.

πŸ‘ 1
borkdude 2020-10-13T09:47:04.387800Z

http://babashka.io helper namespace with parse-edn-stream, etc could work

borkdude 2020-10-13T09:47:38.388Z

which then deprecates these flags over time

2020-10-13T09:48:10.388200Z

Makes sense. Would also make example scripts more explicit (no need to guess the flag settings)

borkdude 2020-10-13T09:49:10.388400Z

(http://babashka.io/edn-stream) (assumes input is in) (http://babashka.io/edn-stream reader) (explicit reader) same for json, transit

borkdude 2020-10-13T09:49:41.388600Z

and these functions could be part of the user namespace for one-liners

borkdude 2020-10-13T09:49:58.388800Z

echo '{:a 1} {:a 2}' | bb '(edn-stream)'

🀘 1
borkdude 2020-10-13T09:51:02.389Z

echo '{:a 1} {:a 2}' | bb '(edn-stream *in*)' for more explicitness

borkdude 2020-10-13T09:51:14.389200Z

(maybe better to be explicit anyway)

2020-10-13T09:52:23.389400Z

Nice πŸ™‚ And this lib could potentially be used from the jvm as well (I guess). So would make it easier to reuse both directions

borkdude 2020-10-13T09:52:36.389600Z

yeah

2020-10-13T10:12:54.389900Z

@borkdude Thanks for listening πŸ™‚

borkdude 2020-10-13T10:14:49.390100Z

:thumbsup: