Andy's Cafe

My Babashka Cookbook

Last updated :

This post is the one I want to reference when I do something in `babashka` and forget syntax or something.

Capture a process's `STDOUT` and process the string after

  (require '[babashka.process :as bp])

  (-> (bp/shell {:out :string} "do-the-thing")
      :out
      (clojure.string/split #"\n"))

Import a local library

Dynamically

You can add deps as one of the first steps in your scripts.

  (require '[babashka.deps :as deps])
  (deps/add-deps
   '{:deps {io.github.lispyclouds/bblgum {:git/sha "1d4de3d49b84f64d1b71930fa1161f8d2622a4d9"}
            dev.andylu/clj-lipgloss      {:local/root "/home/andy/git/clj-lipgloss"}}})

  (require '[bblgum.core :as b])
  (require '[clj-lipgloss.core :as lg :use [log-info log-warn log-debug]])

Statically

Put this in a `bb.edn`

  {:deps {io.github.lispyclouds/bblgum {:git/sha "1d4de3d49b84f64d1b71930fa1161f8d2622a4d9"}
          dev.andylu/clj-lipgloss      {:local/root "/home/andy/git/clj-lipgloss"}}}

And then in your code

  (require '[bblgum.core :as b])
  (require '[clj-lipgloss.core :as lg :use [log-info log-warn log-debug]])

Building a CLI tool

Super basic call

A basic call to `babashka.cli/parse-args`

  (cli/parse-args *command-line-args*)
  alu-field-selection new-catalog -o new-catalog-selected
  {:args [new-catalog], :opts {:o new-catalog-selected}}

Adding a help command

For parsing the help flag, I like to just do a first pass and look the `-h` or `–help`

  (defn show-help []
    (println "TODO"))

  (defn passed-help-arg? [args]
    (let [options (cli/parse-args args {:alias  {:h :help}
                                        :coerce {:help :boolean}})]
      (when (-> options :opts :help)
        (show-help)
        (System/exit 0))))

  (when (= *file* (System/getProperty "babashka.file"))
    (passed-help-arg? *command-line-args*)

    (apply main *command-line-args*))

I think it's a nice approach to say my script will never try to handle your other input if there is a help arg passed in.

Supporting short and long option flags

  (cli/parse-args args
                  {:alias {:o :output}})
  alu-field-selection new-catalog --output new-catalog-selected
  {:args [new-catalog], :opts {:output new-catalog-selected}}

Coerce args to opts

Sometimes you want to consider any args passed in to be grouped into some option keys

  (cli/parse-args args
                  {:alias      {:o :output}
                   :args->opts [:input]})
  alu-field-selection new-catalog --output new-catalog-selected
  {:opts {:input new-catalog, :output new-catalog-selected}}

Requiring input

You can cause the script to exit when certain input isn't given

  (defn show-help [& args]
    (println "TODO Write show-help"))

  (defn exit-on-error [_]
    (show-help)
    (System/exit 1))

  (cli/parse-args args
                  {:alias      {:o :output}
                   :args->opts [:input]
                   :require    [:input]
                   :exec-args  {:output "NEW-catalog"}
                   :error-fn   exit-on-error})
  alu-field-selection
  TODO Write show-help

This lets us reuse the help function we already wrote and by adding a custom `:error-fn`, we can avoid dumping a stack trace for the end user.

Babashka can give a little context to `exit-on-error` too.

  (defn exit-on-error [{:keys [msg]}]
    (println msg)
    (show-help)
    (System/exit 1))
  alu-field-selection
  Required option: :input
  TODO Write show-help

Interactive fuzzy file picker

By using `gum input` with a placeholder, we can show the user some default directory. If that's the one they want, then hitting enter will return an empty string to our script. Couple that with the `(or … default-dir)` and you can actually save the `default-dir` as the choice.

To get the fuzzy find, we use `gum filter`. It expects newline separated input which is what `babashka.process/shell` spits out. Finally, for the `filter`, I set a `:height 10` so that `gum` doesn't try to fill the whole screen when it gets a ton of input

  (let [default-dir (System/getenv "PWD")
        chosen-dir  (or (-> (b/gum :input
                                   :placeholder default-dir)
                            :result
                            first)
                        default-dir)
        files       (-> {:out :string}
                        (bp/shell "ls" chosen-dir)
                        :out)]
    (->> (b/gum :filter :height 10 :in files)
         :result
         first))

Reply via email

Tags

#babashka   #written-in-org-mode