@curiouslearn clojure -m entry.point
assumes you have the source of project at hand (i.e., on your production server), and that the Clojure CLI is installed (on your production server), and that you are running it in the project directory. This is a reasonable approach for example if you have your code checked out from version control on the server and you deploy new versions by running git pull
on the server (which some people do) and just just restarting or reloading your project. java -cp the.jar clojure.main -m entry.point
assumes you have a JVM on the production server (which you must have for the Clojure CLI too) and that you have the.jar
on the server -- but you don't need the project structure (source code) and you don't need the Clojure CLI installed.
In the latter case, you could build the.jar
somewhere else and deploy just that file to the server (by whatever method you choose).
We have one project in production that is deployed as source (legacy ColdFusion code that loads Clojure code at startup -- so we have Clojure CLI installed, plus all our Clojure source code, plus all our CFML source code). All our other projects are deployed just as AOT'd JAR files, and even tho' they all have a primary entry point, several of them have multiple -main
functions (in different namespaces) so java -jar uberjars/worldsingles.jar
runs a default process (built with depstar
and -C -m worldsingles.publisher
) but that JAR has dozens of -main
functions and all those others are run via java -cp uberjars/worldsingles.jar clojure.main -m some.process
for the various namespaces. That's mostly how we run our cron jobs, for example.
Awesome! Thank you. It is all much clearer now. I have so far always deployed using git pull
. This has been really helpful.
You're already doing things (one of) the Clojure way then š
(having the Clojure CLI installed on your servers can be useful for all sorts of ad hoc server maintenance tasks so that's definitely a path to consider)
Oh, and one final thing about JAR files that may not be obvious: java -cp the.jar clojure.main
with no additional arguments will start a plain console REPL, with everything in the JAR file as your available dependencies. That can be useful too at times.
Big Sur installation ā can you guys help, please? Iām getting this error, despite having latest command line tools etc.
==> Installing clojure from clojure/tools
==> Downloading <https://download.clojure.org/install/clojure-tools-1.10.1.561.tar.gz>
Already downloaded: /Users/josefrichter/Library/Caches/Homebrew/downloads/e7ac82ad2d40c6edab636f088e6ea8c92552eb07eb1736d0e36d55818e6bc4e7--clojure-tools-1.10.1.561.tar.gz
Error: Your CLT does not support macOS 11.0.
It is either outdated or was modified.
Please update your CLT or delete it if no updates are available.
Error: An exception occurred within a child process:
SystemExit: exit
Good morning everyone! Nice to meet you all! I want to start learning a new language and I am thinking about Clojure. I have functional programming background. The purpose of learning it is to use it in my existing company's stack. We are a JVM company. Any good book for the newcomer?
this is a brew exception. check brew doctor
it usually has some useful information.
ah, it tells me Iām out of luck, basically š
maybe check if xcode-select -p
points to most recent command line tools
@pmamatsis as I heard there are many good ones. I really liked https://www.braveclojure.com/ and you can read it online for free
that gives me /Library/Developer/CommandLineTools
but CLT for beta releases usually has āBetaā substring in path
I have clean install of Big Sur at the moment, so I shouldnāt have any older CLT.
I even downloaded the latest CLT published yesterday
aha, then I can recommend to install xcode12 and point xcode-select to itās CLT. Unfortunately apple forces beta users to install xcode to have up-to-date tools(
> point xcode-select to itās CLT how do I do that please?
I mean, how do I find out where exactly to point it? should I see two separate installations of CLT?
xcode-select -s /Applications/Xcode.app/Contents/Developer
only replace http://Xcode.app with whatever you vave installed as Xcode12
it could be http://XcodeBeta.app or something else
I can also recommend Brave Clojure. I also read https://pragprog.com/titles/roclojure/getting-clojure/, but it doesn't go as deep in the material as Brave. Next book will be https://pragprog.com/titles/vmclojeco/clojure-applied/, since it talks about working on a project.
well at this point itās actually official Xcode 12 distributed via appstore. Itās not beta anymore. Only Big Sur is beta. So I ran the command you gave me, which probably didnāt cause any change, and the problem unfortunately persists š
damn apple betas š
I was planning to upgrade this evening )
well be careful. I had to wipe clean my macbook and it took 6 attempts to install successfully š
why wipe? is it required now?
my installation broke down and in recovery mode I was unable to unlock filevault, despite knowing the password. seems like very unfortunate bugā¦ so the only way was to wipe clean and run clean install
luckily I have everything important in Dropbox, so no big deal
good to know! will do a backup right before
well, I donāt really have anything important in the first place š
@michelemendel oh clojure applied looks great! Gotta put it on my to-read list
they released beta7 today. so maybe it will be better again
I'm also reading Elements of Clojure. This is a short book talking about interesting things on a different level, like how to name things. https://www.amazon.co.uk/Elements-Clojure-Zachary-Tellman/dp/0359360580/ref=rtpb_2/261-0092455-2808276?_encoding=UTF8&pd_rd_i=0359360580&pd_rd_r=d4d0c618-5144-445b-a6b6-e10bcf0c5863&pd_rd_w=8AD50&pd_rd_wg=W0tME&pf_rd_p=efeabfe0-1c83-4a3f-b509-7abd424941d1&pf_rd_r=FJBKYETFXYNQFTC3DSJM&psc=1&refRID=FJBKYETFXYNQFTC3DSJM
The Command Line Tools package for Xcode 12.2 beta isnāt currently available. (69012274) I found this in known issues for beta7 (
https://practicalli.github.io/ has books and 80+ hours of videos to help learn the Clojure language and development with Clojure. Content regularly added... Feel free to ask questions about the content in #practicalli
hmā¦ I think it is possible to install everything manually you need this https://download.clojure.org/install/clojure-tools-1.10.1.561.tar.gz unpack cd clojure-tools ./install.sh /usr/local
aah that opens its own set of problems with /usr/local not being writable, and cannot be changed
I think prefix is not significant here. It could be any place available for you to write and added to your PATH
sorry for dummy question, where would you typically put it, please? itās a bit low level for me
do you have ruby installed?
ruby --version
should tell you that
yes, that comes with macOS I think
2.6.3p62
nice
mkdir -p $HOME/usr/local/Cellar/clojure/1.10.1.561
HOMEBREW_RUBY_PATH=$(which ruby) ./install.sh $HOME/usr/local/Cellar/clojure/1.10.1.561
ln -s "$HOME/usr/local/Cellar/clojure/1.10.1.561/bin/clojure" "$HOME/usr/local/bin/clojure"
ln -s "$HOME/usr/local/Cellar/clojure/1.10.1.561/bin/clj" "$HOME/usr/local/bin/clj"
then you need to add ā$HOME/usr/local/binā to your PATH
thank you. I fail here:
āÆ ./install.sh $HOME/usr/local/Cellar/clojure/1.10.1.561
./install.sh: line 14: -pi.bak: command not found
I changed my script to support that
sorry
submit solution too early
argh, you also need to run mkdir -p $HOME/usr/local/bin
you are the king!!!
thank you, seems to be running!
Nice, glad that it is resolved) later, once everything will be settled on homebrew side, you can simply remove $HOME/usr
it all works like charm, running my tiny webapp smoothly now. thank you once again š
š»
Thank you all so much for your answers! I will be starting on tonight! Please bear with me! I will come back with more questions! :)
@pmamatsis I am beginner to clojure too. Since you know Java you are likely in a much better situation than I. But the two I liked are: Clojure Workshop and Living Clojure.
I also recommend listening to Eric Normandās podcasts.
Hey friends, is it typical when building a clojure API to pass data to the (also clojure) frontend as json, or plaintext? Or, is there a way to do edn directly?
check out transit ā https://github.com/cognitect/transit-format
https://github.com/cognitect/transit-clj https://github.com/cognitect/transit-cljs
Thanks!
Hi! I want to share this GIT repository that might be useful for beginners. This is the useful usermanager example of @seancorfield (https://github.com/seancorfield/usermanager-example/) but for which for learning purposes, I started again from scratch by replacing the Compojure librarie by Reitit and Component by Integrant : https://github.com/PrestanceDesign/usermanager-reitit-integrant-example Cheers!
So im doing some refactoring work(mostly Im trying to remove unwanted dynamism). I started to run into glorious cyclic dependency problem. It is kind of prominent in clojure since namespaces get initialized with require -> for instance in python import only references a namespace, it gets initialized on first access/invokation from that ref. Is there any sane way of dealing with stuff like this?
@roguas Im pretty new to clojure but I believe the problem is often the same. Cyclic dependencies often show that things depend on each other which shouldnt. One way this is often tackled is by using a separate namespace for protocols and alike
Ive read some discussions regarding this and somehow understand certain underlying problems with that. Problem is that some things are in fact reliant on each other in this way and it is kindof pointless to redesign for compilation only. Like in the python example, you can do
a.py import b
b.py import a
Yes, that does not work in clojure or as I had the problem too, in golang. One part of me tells me this is good though. The redesign paid out in the end as things got decomplected
The general way to go is then that different namespaces talk to each other over abstractions from a separate namespace and things which really belong together go in the same namespace
Is it possible to do require
during runtime? I am trying but keep getting Unable to find static field
what are you trying?
@dpsutton so currently I can kick the can down the road, by defering requiring a namespace into function. Since the namespace I want available has only one symbol/definition.
; a.clj
(ns a)
(def y 3)
(defn get-x []
(require `(b))
b/x
)
; b.clj
(ns b (:require [a :as a])
(def x 2)
I am trying to avoid this problem as it currently requires too much refactoring for the chunk I am about to clean up. So I want to defer namespace initialization till a function gets invoked.
This way, whenever a/get-x gets called b will already by initialized (so skipped)
someone had a similar problem yesterday. require is runtime but still clojure is trying to compile b/x
and there is no b. you can do (requiring-resolve 'b/x)
(where b/x is the fully qualified name of x) or, if you're sure that b has been loaded you could just use the fully qualified path. both of these are hacks around the design and fixing that would most likely be your best bet
Can someone please explain what is the difference between actors and agents? I can't quite understand the difference.
probably helpful for perspective
Still kinda stuck is this the way to go?:
; a.clj
(ns a)
(def y 3)
(defn get-x []
(requiring-resolve 'b/x)
b/x
)
check the docstring on requiring-resolve
it returns the var and then you would use it. (let [f (requiring-resolve 'b/x)] (f))
If I eval a symbol without any namespace:
(eval '>)
(defn f [] (+ 1 1))
(eval 'f)
To my surprise, eval does the right thing: it finds clojure.core/>
, and finds my.namespace/f
Is there an article on how clojure resolves symbols? Would love to read it!
well Clojure wouldn't work if eval didn't do the right thing, so that should be surprising :)
you are always in a current namespace per *ns*
that namespace has a set of "referred" names, aliases, etc
(find-doc #"ns-")
to find a bunch of functions that let you explore that info (ns-refers etc)
importantly clojure.core is auto-referred by ns
so everything in core is always available unqualified
Ah, I see, thanks Alex! Will poke around with (find-doc #"ns-")
This is why I stopped upgrading my Mac and stayed on 10.12 but now a lot of new software won't install because it's too old š So I'm switching to Windows and WSL2. So tired of Apple breaking my dev environment!
(and I've been an Apple customer for 30 years!)
thanks, (var-get) got me to goodworkable place
my mac survived 10.6 -> 10.15.6 of rolling updates of software + Air 2012 -> MacBook Pro 2019 updates of hardware but upgrade XP -> Vista took too much mental energy then I gave up on windows)
Question for the Gurus out there - building a simple API that pulls users info from Datomic. Function in question is this one: First run it returns everything fine, if the database is updated via another call and this function is run again - it seems to be returning cached results. How do I get it to re-evaluate it each run?
(defnĀ find-user-by-username-or-email-allĀ [dbĀ username-or-email]
Ā Ā (d/q
Ā Ā Ā '[:findĀ (pullĀ ?eĀ [*])Ā .
Ā Ā Ā Ā Ā :inĀ $Ā ?user-or-email
Ā Ā Ā Ā Ā :whereĀ (orĀ [?eĀ :user/usernameĀ ?user-or-email]
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā [?eĀ :user/emailĀ ?user-or-email])]
Ā Ā Ā db
Ā Ā Ā username-or-email))
(defnĀ getUserĀ [username-or-email]
Ā Ā (letĀ [idĀ (find-user-by-username-or-email-allĀ dbĀ username-or-email)]
Ā Ā Ā Ā {:idĀ id}))
{
"id": {
"user/email": "john...",
"user/password-reset-sent": "...",
"user/username": "john",
"user/send-updates?": false,
}
}
Now we update username to something else and the getUser still returns:
{
"id": {
"user/email": "john...",
"user/password-reset-sent": "...",
"user/username": "john",
"user/send-updates?": false,
}
}
@seancorfield itās not that bad, itās just not a good idea to install beta versions of operating systems if you want to be conservative š so I knew and embraced the risks. but if you find peace and pleasure on windows, then why not. microsoft is making huge leaps in developersā satisfaction.
@thdm are you passing the same db value each time?
Datomic db values are immutable
Yes so the input in this case would be (getUser "john")
So to query for data that has been added since your first call to d/db
you need to call it again to get a new db value
I'm not understanding - I have to call the getUser twice?
No, the call to d/db
To get a database value
The db you're passing into the query
Give me a minute I'll make an example
db is defined as:
(def uri (:datomic-url e/env))
(def conn (d/connect uri))
(def db (d/db conn))
Right. That value of db is immutable
It is a db value as of the exact moment you called d/db
So anything you transact into the db after that point in time will not be in that original db value
so I have to redefine db each call then?
It depends what you're doing. You may want to keep a specific db value for several operations
So that you know they're all using the same consistent point in time db value
aww ok - got a code snippet to re-evaluate db each call? LOL omg.
See https://docs.datomic.com/on-prem/best-practices.html#consistent-db-value-for-unit-of-work
In this case I want the latest - basic CRUD operations
@delaguardo I've only become a fan of Windows since 8.1 (but I've been forced to use every version since 3.11). I've always had Macs. I started on 680x0 chips, went through the PowerPC migration, the Intel migration. I've had over half a dozen laptops and over half a dozen desktops. Back in the System 6 & 7 days I ran MachTen (Tenon Intersystems' BSD variant) as a "parasitic" Unix O/S alongside Apple's O/S so I've always had the "Mac" interface and *nix under the hood. But almost every single O/S upgrade broke something in my dev setup and over the last 5-8 years Apple seem to have lost interest in developers and swung their focus squarely to consumers and services. I used to absolutely hate MS but over time I found myself preferring their apps on my iPhone, and I bought a cheap Windows laptop in 2012 that has slowly become a more usable developer machine. This year I replaced my iPhone with an Android, and I just bought a Surface Laptop 3, and I hadn't realized just how much of a "captive system" Apple has become. I'm pleasantly shocked at how much MS has embraced open source and how much they are focusing on developer experiences these days.
With WSL, I run Ubuntu for all my dev work on both laptops, while still having the slick UX of Windows 10. I never thought I'd switch from a Mac, when I bought my last iMac in 2012...
if that's the case, you can pass (d/db conn)
to your call to your query function
instead of passing db
So like this?
(defn getUser [username-or-email]
(let [db (d/db conn)
id (find-user-by-username-or-email-all db username-or-email)]
{:id id}))
sure that works too
i might still make db an argument
otherwise you'd have to pass a conn
which is less idomatic
Ok I get where I went wrong
so leave your getUser function alone
and just change where you call it from:
(getUser db ...)
to (getUser (d/db conn) ...
)
ah, sorry i misread your original; That advice ^ still stands though
but having getUser do the "getting a db value" is probably OK too
is your conn
a global var?
yah
generally you'll want to avoid that and pass either a conn or a db value to functions that transact or query, respectively
i.e. maintain the conn in a map that represents your system context (or use something like the component library to help with it)
then your getUser could take a db and a username
and you could invoke with something like:
(getUser (d/db (:conn my-ccontext-map)) "username i care about")
Re-arranged it to
(defn find-user-by-username-or-email-all [db username-or-email]
(d/q
'[:find (pull ?e [*]) .
:in $ ?user-or-email
:where (or [?e :user/username ?user-or-email]
[?e :user/email ?user-or-email])]
db
username-or-email))
(defn getUser [db username-or-email]
(let [id (find-user-by-username-or-email-all db username-or-email)]
{:id id}))
(GET "/getuser/:n" []
:path-params [n :- s/Str]
;:summary ""
(ok (getUser (d/db conn) n)))
)))
:thumbsup: looks much better consider looking into conn management stuff in the future (i.e. the conn not being a top-level var, but instead created by a function call and passed around as needed) see: https://github.com/Datomic/ion-starter/blob/master/src/datomic/ion/starter.clj
both the peer library and client library cache connections, so you can safely write a "get connection" function that you can invoke from wherever in your application
and you will get back the same connection
I always have archlinux aside from other, more user friendly, OS. That is like steel hardening - I saw so many problems during software upgrade, now they don't bother me at all) maybe you right and Apple is not doing great job for developers, but I just can't share same feeling, probably because of some different usage patterns. But let's do not convert this thread into a battle of vendors our operating systems. Someone had annoying problem, was aware of consequences of beta testing and ask for help - I would never try to convince trying something completely different because of such a minor thing) :cheers:
there is a little bit of extra stuff in that example ^ for ions But you could just as easily have a get-conn function:
(defn get-conn
(d/connect "my-datomic-uri"))
in a peerIndeed š
Gotcah - Thanks a ton - that solved a couple days of head pounding.
:thumbsup:
I should try Arch some day. I hear lots of developers using it and liking it.
Hi! I was wondering if there is a way to get left-over keys from an s/keys
spec. For example, if I have (s/keys :req-un [::name])
and the map {:name "Alice" :age 34}
, and if it's s/valid?
, I would like it to return {:age 34}
. Is there something in Spec that does this? (I don't want to prohibit extra keys, but I'd like to know what they are, if provided.)
(I was able to hack something together using s/describe
.)
@johsgrd I'm curious about your solution, can you post it?
Sure. But it's terrible. And also doesn't handle req-un
vs req
and opt-un
vs opt
.
(defn all-keys
"If given SPEC is a keys spec, return all the keys it knows about."
[spec]
(let [form (s/form spec)
[kind & options] (and (seq? form) form)]
(when (= kind 'clojure.spec.alpha/keys)
(set (mapcat (apply hash-map options)
[:req-un :opt-un :req :opt])))))
(defn leftovers
"If SPEC is a keys spec and X conforms to it, return left over keys."
[spec x]
(when (s/valid? spec x)
(when-let [keys' (all-keys spec)]
(let [extra-keys (set/difference (set (keys x)) keys')]
(zipmap extra-keys (map x extra-keys))))))
Thereās also https://cljdoc.org/d/metosin/spec-tools/0.10.4/api/spec-tools.core#select-spec which does the opposite of what you need. Itās a pretty cool library!