Clojure things I learn
Table of Contents
- 1. clojure
- 1.1. organize code
- 1.2. start an app
- 1.3. deps.edn
- 1.4. tutorials
- 1.5. shadow-cljs
- 1.6. etaoin
- 1.7. string to uuid in postgres pg
- 1.8. add libs live
- 1.9. indentation
- 1.10. lein test
- 1.11. unalias
- 1.12. socket repl
- 1.13. clojure by example
- 1.14. add conditionally to a map
- 1.15. subsets
- 1.16. Where did I leave that stuff?
- 1.17. Array of hashes to hash
- 1.18. Test private functions
- 1.19. managing exceptions in clojure
- 1.20. mocking, stubbing, spying, integrant…
- 1.21. Start a compojure app
- 1.22. Circleci 3part tutorial on duct, aws , terraform
- 1.23. integrant,mount,component. clojurians #off-topic 2021-08-07
- 2. clojure exceptions
- 3. logging
- 4. concat trap
- 5. queryable runtime. "inspectability"
- 6. clojure internals
- 7. data-driven
- 8. simple stu halloway
- 9. cljs
- 10. core.async
- 11. java interop
- 12. tricky flatten
- 13. modern clojurescript
- 14. clojure integrant and the like
- 15. holy lambda!
- 16. web dev
- 17. nested filtering, some, any , every , filter….
- 18. list all implementations of a defmethod
- 19. find all public methods of a namespace
- 20. cli and tools and deps
- 21. dependency injection
- 22. clojure codebases
- 23. REPL large objects
- 24. inspect last result in emacs
- 25. packaging lein deps.edn
- 26. forms
- 27. state machine to parse files
- 28. tap>
- 29. clojure -Sdescribe
- 30. miniupdates in python
- 31. proj from scratche
1 clojure
1.1 organize code
1.2 start an app
1.2.1 lein
lein new app foo
lein repl
lein run
1.2.2 deps.edn
1.2.3 cljs
lein new re-frame foobar +10x edit shadow-cljs.edn add lib npx shadow-cljs watch app cider-connect-cljs local 7888 shadow-select :app .
1.3 deps.edn
1.4 tutorials
- cljs re-frame: https://www.youtube.com/user/VideosDanA
- spa re-frame: https://www.youtube.com/watch?v=yVb8PS6a4Mk&t=666s
- TODO in cljs https://www.youtube.com/watch?v=jAkw6Wb-rTA
- integrant: https://www.youtube.com/playlist?list=PLb5SjnGEsSJdJnbjxVWci1P7mKY-K1ExD
1.5 shadow-cljs
https://jiyinyiyong.medium.com/a-beginner-guide-to-compile-clojurescript-with-shadow-cljs-26369190b786 Shadow-cljs is a tool, like make, or denom. Or lein. That builds packages. Like lein it does the building of the pipeline, but unlike lein, it also has the hot reload thing. So it's like lein+repl-with-hotreload.
Figwheel and figwheel-main are alternatives to it, but they are basically substitutes one for the other.
Clojurescript has shitloads of repls, https://lambdaisland.com/guides/clojure-repls/clojurescript-repls#org8300646 .At this point, I dont' know if shadow-cljs provides its own repl mechanism, or it's on you to say "shadow-cljs, I want you to use rhino as a repl". https://clojureverse.org/t/what-is-the-difference-between-figwheel-main-and-figwheel-sidecar/6832
1.6 etaoin
selenium-like. just from clojure/babashka.
1.7 string to uuid in postgres pg
(ns foo.bar (:import java.util.UUID)) (tdb/update-where! 'Product {:id id} :user-id (java.util.UUID/fromString new-window-id)))
1.8 add libs live
To add libraries live you need the combination of 3 things:
- lein
- clj-commons/pomegranate
- https://www.eigenbahn.com/2020/05/06/fast-clojure-deps-auto-reload
- https://github.com/clj-commons/pomegranate
- https://github.com/clojure/tools.trace
- https://github.com/philoskim/debux
- https://eli.thegreenplace.net/2017/notes-on-debugging-clojure-code/
- https://cognitect.com/blog/2017/6/5/repl-debugging-no-stacktrace-required
1.9 indentation
- https://github.com/kkinnear/zprint
- clj-rewrite
- babashka stuff?
1.12 socket repl
java -Dclojure.server.repl={:port 6666 :accept clojure.core.server/repl} -jar target/uberjar/myuberjar.jar #notice in project.clj there's a :socket profile as well that i use for local dev
1.13 clojure by example
1.14 add conditionally to a map
(merge base-map (when name {:name name}) (when address {:address address}))
similar to CL's `(:a 1 ,@(when foo `(:b 2)))
1.15 subsets
A set is a map with values equal to keys.
Creating a map from a set is trivial, you get {:a :a, :b :b ….}
maps can be converted to sets, and the elements are vectors of [k v]
(set [1 2]) ; #{1 2} (set {:a 1}) ; #{[:a 1]} (set {:a 1}) ; #{[:a 1]} (set/subset? (set {:a 1}) (set {:a 1})) ; true (set/subset? (set {:a 1}) (set {:a 2})) ; false (set/subset? (set {:a 1}) (set [:a 1])) ; false (set/subset? (set {:a 1}) (set [:a 1])) ; false (set [:a 1]) ; #{1 :a} (set [:a :a]) ; #{:a} (set [[:a 1]]) ; #{[:a 1]} (set/subset? (set {:a 1}) (set [:a 1])) ; false (set/subset? (set {:a 1}) (set [[:a 1]])) ; true (set {1 :a}) ; #{[1 :a]} (into {} (set {:a 1})) ; {:a 1}
Even thought there might be a way to create a hash from a set like
this (fun #{:a}) ; => {:a :a}
that would mean that you can
prepare a set and a hash that would be "equal", but from what I see
above, conversions between maps and sets work neatly.
(set/subset? (set {:a 1}) #{:a}) (set/subset? #{:a} (set {:a 1}) ) (set/subset? #{:a} (set [:a]) ) (set/subset? (set [:a]) #{:a}) (set/subset? (set [:a]) #{:a}) ... (set/subset? (set {:a 1}) (set [[:a 1]])); true (set/subset? (set {:a 1}) (set [:a 1])); false {:a 1} (into {} [[:a 1]]); {:a 1}{:a 1}
Instead of checking #(every? (partial contains? %) [:a :b]) vs
#(set/subset? #{:a :b :c} %)
To check for presence of required
keys as parameters, it's very easy and "apl-ish" (set/subset? (set
[:first-name :last-name :company]) (:params request))
1.16 Where did I leave that stuff?
So, I'm debugging something, and I use the inspector to retain some value I want for later.
d myvar
will save that value as myvar
in the current namespace you're in.
Later on, you move namespace and you want to get back that myvar
but you don't remember where it was stored.
cider-apropos myvar
will list you which ns have this defined. so
you can go and fetch it from there.
1.17 Array of hashes to hash
(map (juxt :id identity) [{:id 1 :name "foo"} {:id 2 :name "bar"}])
1.18 Test private functions
If you want to test a private function, you can use the binding
(which is always accessible), and call it.
(ns a.b-test (:use [clojure test])) (deftest a-private-function-test (testing "a private function" (let [fun #'a.b/c] (is (not (fun nil))))))
ref and extended explanations: in stackoverflow.
1.19 managing exceptions in clojure
THere's some good discussion here in clojurians thread.
1.20 mocking, stubbing, spying, integrant…
- https://www.youtube.com/watch?v=fgOfYyTeBaQ
- https://promesante.github.io/2021/04/28/clojure_repl_driven_development_part_1.html
- https://github.com/alexanderjamesking/spy
- https://engineering.fundingcircle.com/blog/2016/01/11/tdd-in-clojure/
- http://www.diva-portal.org/smash/get/diva2:806620/FULLTEXT01.pdf
- http://software-ninja-ninja.blogspot.com/2014/04/5-faces-of-dependency-injection-in.html
- https://clojureverse.org/t/stuart-sierra-components-and-config/7996/7
- https://slipset.github.io/posts/dependency-injection-perhaps
- https://slipset.github.io/posts/config
- https://slipset.github.io/posts/all-your-base
- https://github.com/donut-power/system
1.21 Start a compojure app
1.21.1 lein&ring info and tutorials
1.21.2 There are multiple templates,
1.21.3 Deploy a compojure+ring app
lein new compojure hello-api-clj cd hello-api-clj lein ring uberjar # lein uberjar without 'ring' leaves you at the repl java -jar target/hello-api-clj-0.1.0-SNAPSHOT-standalone.jar
OR
lein new compojure hello-api-clj
cd hello-api-clj
lein ring uberjar
java -jar target/hello-api-clj-0.1.0-SNAPSHOT-standalone.jar
If you were using tools.deps, there's no lein ring, or lein ring uberjar, https://practical.li/clojure-webapps/projects/banking-on-clojure/application-server-configuration.html shows how to do it there.
I'm going for compojure-template for now. No reason why, really.
1.21.4 create
lein new compojure my-stupid-app lein ring server
lein-ring uses a fancy way to start the server, so you don't have an easy entrypoint before the server is kicked (integrant?, mount?, component?).
1.21.5 repl
if you want to start the repl when lein ring server
, add in
project.clj :nrepl {:start? true}
.
lein ring server imports ring and ring-server/ring-server, which run the jetty server for you on start https://github.com/weavejester/lein-ring/blob/master/src/leiningen/ring/server.clj#L34-L35
But if you want control over it, add [ring "1.9.4"] explicitly to your project.clj, start with cider-jack-in, and in the main ns,
(use 'ring.adapter.jetty) ; if you don't require [ring ..] this is not available (defonce server (run-jetty #'app {:port 3000 :join? false})) (.stop server)
1.21.6 DB
1.21.7 Migrations
Migratus provides the lib AND the lein plugin.
You have to add next.jdbc AND the concrete driver for your db sqlite-jdbc in my case.
At this point to debug, I found that if you start from the console
with lein ring server
even if there is a repl, the process
doesn't have the nrepl and cider helpers, so it's not really
useful as a repl to be used from emacs. Interim solution:
cider-jack-in
and not have the server on. Have to figure that
one asap.
1.21.8 Migrations in prod
To run migrations at the start of the app, lein-ring has a config :init and :destroy that run before and after loading the handler.
- https://prasans.info/use flyway with leiningen/
- https://devcenter.heroku.com/articles/clojure-web-application
- Clojure web development essentials (book)
It seems it's the de-facto recommended way, but I still can't see how you would run it only on 1 of the deployed servers, or rollback if the migration fails.
1.21.9 uberjar
lein ring uberjar
produces a jar that on start it actually runs
the jetty thing. lein uberjar
leaves you in the repl.
1.21.10 Security
1.21.11 honeysql and next/jdbc
1.22 Circleci 3part tutorial on duct, aws , terraform
1.23 integrant,mount,component. clojurians #off-topic 2021-08-07
2 clojure exceptions
3 logging
timbre/log provides also spy
and other cool stuff
4 concat trap
5 queryable runtime. "inspectability"
cider-apropos is super useful
(apropos "rename-keys") (clojure.set/rename-keys) (doc clojure.test) (find-doc "dynamic")
for instance, this is our production jar: MB_JETTY_PORT=3006 java -Dclojure.server.repl="{:port 50506 :accept clojure.core.server/repl}" -jar 0.40.0.jar and then just a simple % nc localhost 50506 user=> (apply require clojure.main/repl-requires) nil user=> (apropos "union") (clojure.set/union) user=> (apropos "password") (clj-ldap.client/modify-password metabase.api.session/GET_password_reset_token_valid metabase.api.session/POST_forgot_password metabase.api.session/POST_reset_password metabase.api.user/PUT_:id_password metabase.api.util/POST_password_check metabase.driver.common/default-password-details metabase.email/email-smtp-password metabase.email.messages/send-password-reset-email! metabase.integrations.ldap/ldap-password metabase.integrations.ldap/verify-password metabase.models.database/protected-password metabase.models.user/form-password-reset-url metabase.models.user/set-password! metabase.models.user/set-password-reset-token! metabase.public-settings/enable-password-login metabase.public-settings/password-complexity metabase.util.password/active-password-complexity metabase.util.password/common-passwords-url metabase.util.password/verify-password) user=>
I once wondered how many things were added to clojure.core over the versions. After cloning the Clojure repo, and doing weird regex checks across history in git, I realised I could just do this: user=> (->> (vals (ns-publics 'clojure.core)) #_=> (keep (comp :added meta)) #_=> (frequencies) #_=> (sort-by #(Long/parseLong (subs (key %) 2))) #_=> (run! println)) [1.0 434] [1.1 38] [1.2 45] [1.3 16] [1.4 8] [1.5 11] [1.6 8] [1.7 19] [1.8 1] [1.9 27] [1.10 8] That's when I started to really "get" the value of the queryability. I then understood how easily I could implement my own version of clojure.repl/apropos. Edit: fixed the sorting. (edited)
6 clojure internals
- https://aphyr.com/posts/305-clojure-from-the-ground-up-macros
- https://lambdaisland.com/blog/2021-08-25-classpath-is-a-lie
- http://blog.kdgregory.com/2016/05/how-and-when-clojure-compiles-your-code.html
- https://www.youtube.com/watch?v=2SGFeegEt9E <- decompilation
- https://dev.to/dpsutton/exploring-the-core-cache-api-57al
- https://worace.works/2021/04/13/jar-hell-part-2-jvm-deployment-strategies/
- https://www.deepbluelambda.org/programming/clojure/how-clojure-works-a-simple-namespace
- https://www.deepbluelambda.org/programming/clojure/how-clojure-works-namespace-metadata
- https://leiningen.org/reference.html
7 data-driven
8 simple stu halloway
10 core.async
11 java interop
12 tricky flatten
- https://web.archive.org/web/20190925194705/http://chouser.n01se.net/apply-concat/
- stuart sierra's clojure don'ts
13 modern clojurescript
14 clojure integrant and the like
15 holy lambda!
- lein new holy-lambda holy-lambda-example
- nix-shell -p aws-sam-cli
- aws login
- AWS_PROFILE=my_profile sam local invoke ExampleLambdaFunction –region eu-west-1
- AWS_PROFILE=my_profile sam local start-api –region eu-west-1
- AWS_PROFILE=my_profile sam deploy –guided –region eu-west-1
- http https://1232323abcd.execute-api.eu-west-1.amazonaws.com
- AWS_PROFILE=rgrau sam delete –region eu-west-1
16 web dev
17 nested filtering, some, any , every , filter….
There's a weird nice relation between set/* functions, and using sets as containers for keywords, that are evaluated as functions.
filter + comp + set seems powerful
(->> @evts (map json/decode) (filter (comp #{"invoice.payment_failed" "charge.failed"} #(get % "type")))) (select-keys (->> @evts (map json/decode) (group-by #(get % "type"))) ["invoice.payment_failed" "charge.failed"]) (filter (comp #{"foo" "bar"} :id) [{:id "foo" :n 1 } {:id "bar" :n 2 } {:id "baz" :n 3 }]) (when (#{"charge.failed" "invoice.payment_failed"} (get {"type" "charge.failed"} "type")) "yes") (when (some #{"charge.failed" "invoice.payment_failed"} [(get {"type" "charge.failed"} "type")]) "yes")
18 list all implementations of a defmethod
(keys (.getMethodTable ig/init-key))
19 find all public methods of a namespace
(ns-publics *ns*)
20 cli and tools and deps
21 dependency injection
- https://www.evalapply.org/posts/which-clojure-codebases-to-read-how-and-why/
- https://www.yegor256.com/2014/10/03/di-containers-are-evil.html
- https://softwareengineering.stackexchange.com/questions/19203/what-are-the-benefits-of-using-dependency-injection-and-ioc-containers
- https://www.reddit.com/r/Clojure/comments/6e2o57/how_do_you_manage_global_configuration_options_in/
- https://yogthos.net/posts/2015-10-01-Compojure-API.html
- https://yogthos.net/posts/2015-12-05-LuminusComponents.html
- https://lambdaisland.com/blog/2018-02-09-reloading-woes
- https://slipset.github.io/posts/config
- https://kumarshantanu.medium.com/dependency-injection-with-clojure-using-dime-af57b140bd3f
- https://puredanger.github.io/tech.puredanger.com/2014/01/03/clojure-dependency-injection/
- https://www.juxt.pro/blog/abstract-clojure
- https://clojureverse.org/t/dependency-injection-in-clojure/1723/6
22 clojure codebases
23 REPL large objects
- profiles.clj
:global-vars {*print-length* 5 *print-level* 5}
- overwrite default "to string"
(defmethod print-method martian.core.Martian [^martian.core.Martian v ^java.io.Writer w] (.write w (str "#martian.core.Martian" (prn-str (select-keys v [:api-root])))))
In emacs you might still hit https://github.com/clojure-emacs/cider/issues/2856
(setq cider-repl-use-pretty-printing nil)
24 inspect last result in emacs
m-x cider-inspect-last-result
or m-x cider-inspect-expr *1
25 packaging lein deps.edn
26 forms
27 state machine to parse files
rgm 12:42 AM I'm hunting for possible libraries or techniques for a kind of task that almost certainly has a googleable name, but I don't know it: I have plaintext e-mails coming in and want to act on their message bodies. They're not exactly parseable (ie. I don't think a grammar exists, so it doesn't feel like instaparse would work) and they'd be tedious to regex, though I suppose that might eventually work. Some parts of the body are (probably accidentally) TSV as a result of doing layout in plaintext.I guess I'm after … some data-defined way to establish zones relative to consistent landmarks, and say, ok, use this regex on the line 2 lines later and give me the groups. I can imagine doing a bunch of this with drop-until landmark, etc. working at the level of a line-seq.But I'm wondering if there are good approaches for almost-structured data that operates above lines and regexes and seqs and below formal grammars and parsing. hiredman
12:50 AM a state machine 12:53 (reduce (fn [[state-name data] line] (case state-name ..do stuff with line and data based on the content of line and the value of state-name and return a possibly new state…)) [:init {}] lines). like if you are in :init and see a blank line then move to :after-email-headers or whatever, if you are in :after-email-headers and you see the columns headers for the tsv then move to :accumulate-tsv, and in :accumulate-tsv add the lines to data until a line is blank
28 tap>
(-> {:q []} (update :q conj "a") (update :q conj "b")) (def ml (atom {:q []})) ;;(remove-tap clojure.pprint/pprint) (add-tap #(swap! ml update :q conj %)) (loop [[hd & tl] [1 2 3]] (tap> hd) (when tl (recur tl)))
get(dict, k) update(dict, k, f, args**) reset(dict, k, val, args**) inc update(d,'foo', inc)
29 clojure -Sdescribe
You'll see that there's
30 miniupdates in python
import copy def update(d, k, f, *args): _c=copy.deepcopy(d) _c[k]=f(d[k], *args) return _c def myappend(a,v): _a=a.copy() _a.append(v) return _a d={'a': [1,2,3]} update(d, 'a', myappend, 'hola') def swap(f): return lambda x,y: f(y,x) def swap(f): return lambda *args: f(*reversed(args)) list(update(d, 'a', swap(map), inc)['a'])
31 proj from scratche
- create deps.edn -> {:deps …}
- nix-shell -p neil
- neil add build
- neil dep add com.github.seancorfield/honeysql
- 3 things to build an uberjar:
- (:gen-class) in the main
- def lib
- add :main 'clojaws.core