February 16, 2021
By: Kevin

babashka的pods

babashka是一个基于Graalvm的clojure解释器, 避免了传统JVM启动慢, 内存消耗高的问题. 非常适合写脚本.

我们blog已经有一篇用babashka进行文本处理的例子

最近这个项目演进迅速(捐助者多). 最新两个引入了两个很吸引我的功能

  1. 使用tools.deps引入大量的clojure兼容库
  2. pods可以把任意的第三方库/程序, 封装为可以被babashka使用的库, 极大扩展了使用领域.

下面用honeySQL和sqlite-go, 分别进行了展示.

babashka对tools.deps的支持

clojure官方的依赖管理工具, 可以动态加入依赖(执行时).

tools.deps是个值得单独介绍的工具, 随后专门写一篇.

以sqlite为例子, 下面的脚本动态载入了honysql (一个动态生成sql的库), 实现了本地sqlite数据库通过shell的CRUD.

#!/usr/bin/env bb
;; Hashbang开头, 和其他脚本并无区别, 保证可以直接执行

(ns example
  (:require [babashka.deps :as deps]
            [clojure.data.csv :as csv]
            [clojure.java.io :as io]
            [clojure.java.shell :refer [sh]]
            [clojure.pprint :as pp]
            [clojure.string :as str]))

(def db "/tmp/analysis.db")

(defn query [& args]
  (apply sh "sqlite3" "-quote" "-header" db args))

;;此处动态的载入 HoneySQL
(deps/add-deps '{:deps {seancorfield/honeysql {:mvn/version "2.0.0-alpha2"}}})

(require '[honey.sql :as sql]
         '[honey.sql.helpers :as h])

(def crate-sql
  (sql/format
   (-> (h/create-table :animals)
       (h/with-columns [[:eng-name :text]
                        [:chi-name :text]]))))

(defn create-db! []
  (when-not (.exists (io/file db))
    (query (first crate-sql))))

(defn insert-sql [animals]
  (sql/format (-> (h/insert-into :animals)
                  (h/values animals))
              {:inline true}))

(comment
  (insert-sql [{:eng-name "Bison" :chi-name "水牛"}
               {:eng-name "Duck" :chi-name "鸭子"}])
  (query "INSERT INTO animals (eng_name, chi_name) VALUES ('a', 'b')")
  )


(defn insert-rows! [animals]
  (let [sql                 (insert-sql animals)
        {:keys [exit err]}  (query (first sql))]
    (when (not (zero? exit))
      (println "Error insert var!" err))))

(.delete (io/file db))

(create-db!)

(insert-rows! [{:eng-name "Bison" :chi-name "水牛"}
               {:eng-name "Duck" :chi-name "鸭子"}])

(defn csv-data->maps [csv-data]
  (map zipmap
       (->> (first csv-data) ;; header
            (map keyword)    ;; unless you want string keys instead.
            repeat)
       (rest csv-data)))

(defn all-animals []
  (let [csv-string (-> (query (str/join " " ["select * from animals"]))
                       :out)
        reader (java.io.StringReader. csv-string)]
    (with-open [rdr reader]
      (doall (csv-data->maps (csv/read-csv rdr :quote \'))))))

(pp/print-table (all-animals))

babashka对pods的支持

pods是一些外部程序, 可以作为clojure的libary被babashka使用, pods本身可以用任何语言写成.比如下面的例子使用了go语言对sqlite的封装.

所以数据库操作不需要通过shell, 而是函数调用来完成, 省掉了组装sql和解析sql返回数据解析的过程, 也不需要在中间生成额外的shell进程.

#!/usr/bin/env bb
(ns example
  (:require [babashka.deps :as deps]
            [babashka.pods :as pods]
            [clojure.data.csv :as csv]
            [clojure.java.io :as io]
            [clojure.pprint :as pp]
            [clojure.string :as str]))

(def db "/tmp/analysis.db")

;; sqlite pod, golang写的

(pods/load-pod 'org.babashka/go-sqlite3 "0.0.1")
;; 执行上一语句本地会产生一个pod `~/.babashka/pods/repository/org.babashka/go-sqlite3`
(require '[pod.babashka.go-sqlite3 :as sqlite])

(deps/add-deps '{:deps {seancorfield/honeysql {:mvn/version "2.0.0-alpha2"}}})

(require '[honey.sql :as sql]
         '[honey.sql.helpers :as h])

(def crate-sql
  (sql/format
   (-> (h/create-table :animals)
       (h/with-columns [[:eng-name :text]
                        [:chi-name :text]]))))

(defn create-db! []
  (when-not (.exists (io/file db))
    (sqlite/execute! db crate-sql)))

(defn insert-sql [animals]
  (sql/format (-> (h/insert-into :animals)
                  (h/values animals))))

(defn insert-rows! [animals]
  (let [sql                 (insert-sql animals)]
    (sqlite/execute! db sql)))

(.delete (io/file db))

(create-db!)

(insert-rows! [{:eng-name "Bison" :chi-name "水牛"}
               {:eng-name "Duck" :chi-name "鸭子"}])

(defn all-animals []
  (sqlite/query db ["select * from animals"]))

(pp/print-table (all-animals))

脚本执行

$time bb scripts/sqlite-pod.clj 

| :eng_name | :chi_name |
|-----------+-----------|
|     Bison |        水牛 |
|      Duck |        鸭子 |

0.40s user 0.07s system 104% cpu 0.454 total
Tags: clojure babashka