Quick references for things I commonly do in Clojure including keybindings, project setup, and code snippets.
Command | Keybinding |
---|---|
start repl | `C-c M-j` |
switch namespace (from file) | `C-c M-n n` |
eval top level | `C-c C-c` |
eval defun | `C-M-x` |
eval last | `C-c C-e` |
eval buffer | `C-c C-k` |
lookup clojure docs | `C-c C-d c` |
toggle repl once open | `C-c C-z` |
close repl connect | `C-c C-q` |
run loaded tests | `C-c C-t l` |
run all tests | `C-c C-t p` |
Command | Keybinding |
---|---|
forward sexp | `C-M-f` |
backward sexp | `C-M-b` |
into sexp forward (down into) | `C-M-d` |
out sexp backward (back out) | `C-M-u` |
into sexp backward (back into) | `C-M-p` |
out sexp forward (up out) | `C-M-n` |
delete sexp forward (with parens) | `C-k` |
slurp forward | `C-)` |
barf forward | `C-}` |
slurp backward | `C-(` |
barf backward | `C-{` |
Command | Keybinding |
---|---|
instrument function | `C-u C-M-x` |
next step | `n` |
step into | `i` |
step out | `o` |
eval code in current context | `e` |
inspect current value | `p` |
inspect arbitrary expression | `P` |
inspect locals | `l` |
toggle locals display | `L` |
show stack | `s` |
quit | `q` |
here (place cursor and execute to point | `h` |
Create a file called deps.edn
in a project. The file will contain a map.
{}
Add your source paths.
{:paths ["src/cookbook"]}
Then add some dependencies. Dependencies can be git or maven repos.
{:paths ["src/cookbook"]
:deps {org.clojure/clojure {:mvn/version "1.10.1"}}}
Define test paths and dependencies which will not get built into the source jar. You can also specify the uberjar for packaging. These are defined as aliases in the deps file.
{:paths ["src/cookbook"]
:deps {org.clojure/clojure {:mvn/version "1.10.1"}}
:aliases {:test {:extra-paths ["test/cookbook"]
:extra-deps {lambdaisland/kaocha {:mvn/version "0.0-529"}}
:main-opts ["-m" "kaocha.runner"]}
:uberjar {:extra-deps {uberdeps {:mvn/version "0.1.4"}}
:main-opts ["-m" "uberdeps.uberjar" "--target" "target/cdeps-0.1.0.jar"]}}}
Running the aliases is done as such:
clj -A test
clj -A uberjar
(:require [clojure.pprint :as pp])
(pp/pprint {:hello "there"})
pretty print to file:
(spit "file.edn" (with-out-str (pp/pprint {:hola "buenos dias"})))
Write pretty printed edn to a file:
(spit "file.edn" (with-out-str (pp/pprint {:hola "buenos dias"})))
Read edn from a file:
(read-string (slurp "file.edn"))
Functions for interacting with APIs using JSON payloads:
(:require [clj-http.client :as client])
(defn api-get []
(let [rsp (client/get "https://url.test"
{:headers {"Authorization" "hello"}
:cookie-policy :standard
:accept :json
:as :json})]
(get-in rsp [:body])))
(defn api-post [path body]
(let [rsp (client/post "https://url.test"
{:headers {"Authorization" "hello"}
:form-params {:field "1" :other 2}
:accept :json
:as :json
:content-type :json})]
(get-in rsp [:body])))
The snippet below is taken directly from a project, but gives a decent overview of websocket usage.
(:require [gniazdo.core :as ws])
(defn- create-socket-on-receive
[user-on-receive]
(if (nil? user-on-receive)
(constantly nil)
(fn [msg]
(-> msg
utils/json->edn
user-on-receive))))
(defn- create-socket-on-binary
[user-on-receive]
(if (nil? user-on-receive)
(constantly nil)
(fn [binary-msg offset len]
(->> binary-msg
;; (drop offset)
;; (take len)
String.
utils/json->edn
user-on-receive))))
(defn- get-socket-connection
[opts]
(ws/connect
(:url opts)
:on-connect (or (:on-connect opts) (constantly nil))
:on-receive (create-socket-on-receive (:on-receive opts))
:on-binary (create-socket-on-binary (:on-receive opts))
:on-close (or (:on-close opts) (constantly nil))
:on-error (or (:on-error opts) (constantly nil))))
(defn send-websocket-authenticate [connection]
(->> {:action "authenticate"
:data {:key_id api-key
:secret_key secret}}
utils/edn->json
(ws/send-msg connection)))
(defn send-websocket-listen [connection]
(->> {:action "listen"
:data {:streams ["updates"]}}
utils/edn->json
(ws/send-msg connection)))
(defn create-websocket-connection [opts]
(let [connection (get-socket-connection opts)]
(send-websocket-authenticate connection)
(send-websocket-listen connection)
connection))
(defn close-websocket-connection
[connection]
(ws/close connection))
(:require [clojure.data.json :as json])
(defn edn->json
[edn-content]
(json/write-str edn-content))
(defn json->edn
[json-content]
(json/read-str json-content :key-fn keyword))
(:require [clojure.data.codec.base64 :as b64])
(defn- base64->bytes
"convert a base64 string into a bytes object."
[b64str]
(-> b64str
(.getBytes "UTF-8")
(b64/decode)))
This has not worked great, but is a basic logger
(:require [clojure.tools.logging :as log])
(log/info msg)
Simple SQLite database connection example
;; lein deps but can easily be converted to deps.edn
[com.github.seancorfield/next.jdbc "1.2.761"]
[org.xerial/sqlite-jdbc "3.36.0.3"]
This implementation was a simple key-value store but illustrates how it is done.
(:require [next.jdbc :as jdbc])
(:require [next.jdbc.plan :as plan])
(defn open [db-str]
(let [db-spec {:jdbcUrl db-str}
ds (jdbc/get-datasource db-spec)]
(jdbc/execute! ds ["CREATE TABLE IF NOT EXISTS kvstore(
skey TEXT PRIMARY KEY,
namespace TEXT,
value BLOB);"])
ds))
(defn write-value [ds key namespace value]
(jdbc/execute! ds
["INSERT OR REPLACE INTO kvstore VALUES(?, ?, ?);"
key
namespace
(pr-str value)]))
(defn read-value [ds key]
(let [res (plan/select-one! ds :value ["SELECT value FROM kvstore WHERE skey=?;"
key])]
(if (nil? res)
nil
(read-string res))))
HoneySQL is a library for creating and formatting sql statements using sexp. It makes it very easy to construct statements. Below is an example of using it for a article link storage system:
(:require [honey.sql :as sql]
[honey.sql.helpers :refer [select
from
where
limit
insert-into
columns
values
create-table
with-columns]])
;;
;; CREATE TABLE IF NOT EXISTS articles(
;; id INT AUTO_INCREMENT PRIMARY KEY,
;; source VARCHAR(255) NOT NULL,
;; timestamp INT NOT NULL,
;; title VARCHAR(1024),
;; link VARCHAR(2048));
;;
(defn create-articles-table []
(-> (create-table :articles :if-not-exists)
(with-columns [[:id :int :auto-increment :primary-key]
[:source [:varchar 255] [:not nil]]
[:timestamp :int [:not nil]]
[:title [:varchar 1024]]
[:link [:varchar 2048]]])
(sql/format)))
;;
;; INSERT INTO articles(id, source, timestamp, title, link)
;; VALUES (?, ?, ?, ?, ?);
;;
(defn insert-article [source title link]
(let [now (quot (System/currentTimeMillis) 1000)]
(-> (insert-into :articles)
(columns :source :timestamp :title :link)
(values [[source now title link]])
(sql/format))))
;;
;; SELECT * FROM articles
;; WHERE source=?
;; ORDER BY date DESC
;; LIMIT 20;
;;
(defn select-by-source [source num-articles]
(-> (select :*)
(from :articles)
(where [:= :source source])
(limit num-articles)
(sql/format)))
(try
(do-something)
(catch Exception e
(handle-error)))