beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
piyer 2021-04-23T00:26:07.403800Z

Is there a way to run the lein task after the uberjar? e.g java -jar foo-snapshot.jar -task foobar ?

piyer 2021-04-23T00:30:29.405700Z

I have db migration which I need to run before I deploy (ragtime migration). My build process builds the jar, I am trying to figure out how to run db migration from uberjar.

2021-04-23T13:27:05.417400Z

I typically do this by defining a my.project.migrations namespace, and then java -cp my.jar clojure.main -m my.project.migrations arg arg1 arg2

2021-04-23T13:28:03.417600Z

clojure is flexible enough to use its main to run any ns that has a -main, so you can have as many entry points as you like in one jar (but this is all totally outside the world of lein, which is a build / dep tool not an app runner)

2021-04-23T13:28:28.417800Z

maybe - best treated as a build/dep tool rather than an app runner

alexmiller 2021-04-23T00:35:52.406Z

I don't think you can pass main args if you use -jar

alexmiller 2021-04-23T00:36:24.406600Z

you can java -cp foo-snapshot.jar the.main -task foobar though

alexmiller 2021-04-23T00:36:49.407Z

assuming the.main is your ns with a -main and it takes -task foobar

piyer 2021-04-23T00:40:41.407200Z

got it.

Zaymon 2021-04-23T01:37:47.407800Z

Are there any good resources for clojure testing patterns and best practices?

Zaymon 2021-04-25T05:15:30.066600Z

@c.westrom @jumar Thanks I’ll check out both of these

👍 1
West 2021-04-23T03:14:42.408Z

https://practicalli.github.io/clojure/testing/unit-testing/ Perhaps this one.

West 2021-04-23T03:59:58.408600Z

How does one read edn from a file in clojurescript? I usually just slurp.

2021-04-23T15:52:23.452500Z

Using shadow-cljs:

(def fe-config (edn/read-string (shadow.resource/inline "/config/fe-config.edn")))
where the /config/... is on your classpath

2021-04-23T15:58:30.455100Z

It's essentially just the usual - wrapper around slurp in a macro, but with the (amazing) benefit of having changes to that edn file trigger hot reloads

🙌 1
West 2021-04-23T23:28:33.014100Z

@danvingo Dude, this is fucking awesome. Imma try it out now!

West 2021-04-25T01:54:01.065900Z

I’m starting to change my mind. I might end up not using react after all. I started migrating my site to use this generator called https://github.com/OrgPad-com/volcano, and I’m starting to think that the react magic may be useful.

jumar 2021-04-23T05:00:12.408700Z

Not that I agree with all of that but this can be useful: https://github.com/plumatic/eng-practices/blob/master/clojure/20130821-testing-principles.md

piyer 2021-04-23T05:49:34.409600Z

@seancorfield Thank you for a super nice documentation. https://cljdoc.org/d/seancorfield/next.jdbc/1.1.646/doc/datafy-nav-and-schema

😊 1
🙏 1
seancorfield 2021-04-23T05:58:27.410Z

cljs running in the browser? Won’t you need to slurp the file on the server and send it to the client?

seancorfield 2021-04-23T06:05:06.410300Z

Documenting datafy and nav seems very problematic because the semantics are both very poorly documented in Clojure itself and very poorly understood even by people who implement these protocols.

practicalli-john 2021-04-23T06:12:06.410500Z

As an EDN file is Clojure data, the data could instead be put into a namespace in the ClojureScript project as a def and required in the relevant namespace. This assumes you don't need a specific edn file, just the data of course.

West 2021-04-23T06:17:54.410900Z

So here’s what I’m doing. I’m generating a SPA using reagent. I want to be able to have all my data (stuff that I want to swap out later, like names, emails, links, localization) in a separate file. I would just call a cljs file with my data, but I also a clj file to read that data as well. I tried using cljc instead, but unfortunately it doesn’t work.

West 2021-04-23T06:19:34.411100Z

In the end, it’s a static site that I want to create, and I don’t want react in there. I’d much rather have it use hiccup to spit some static html. The only reason I’m using this approach at the moment is because of a couple npm libraries and shadow-cljs. Both shadow and tailwind make it so easy to do iterative design.

practicalli-john 2021-04-23T06:21:15.411300Z

A single data model is my default starting point. It helps keep the overall design of the code simple. If components of a system are very distinct or have little interaction with each other, then it seems more likely that they would be treated as individual sub-systems (or even spilt into their own system), so a specific data model would seem more appropriate. If components are highly integrated, then typically I would expect them to share a common data structure. These are my general guidelines, every system has its own constraints that need to be considered carefully.

🎯 1
practicalli-john 2021-04-23T06:35:10.411600Z

For static sites I have just used figwheel-main +reagent and included http://bluma.io CSS library via the html page from a CDN. No npm overhead. I haven't dived into shadow, sorry. Perhaps more help on the #shadow-cljs channel

Basile 2021-04-23T11:16:56.413600Z

Hi everyone, Would someone be able to point me to beginner resources about building a browser extension in clj or cljs? I cannot find a great deal of things out there and struggling with making my own tooling from existing tools as I’m at the very, very beginning of my learning 🙂

NikolaMandic 2021-04-23T11:58:12.413700Z

I did not do this but my guesses are that if you did not yet check how to build extension without cljs first check that like google how to make browser extension then look how to compile cljs file to javascript then check how to reference variables that webextension would reference from cljs like globals in browser

NikolaMandic 2021-04-23T12:01:17.414Z

so if your extension would in javascript do something like window.navigator bla bla then you can reference same global from clojurescript you just have to compile clojurescript and make sure when you package extension you put everything clojurescript needs in there

prnc 2021-04-23T12:59:28.416500Z

There is chromex:

prnc 2021-04-23T12:59:45.416700Z

https://github.com/binaryage/chromex/ with good documentation

prnc 2021-04-23T12:59:57.416900Z

…and examples https://github.com/binaryage/chromex/tree/master/examples

prnc 2021-04-23T13:02:04.417100Z

From my limited experience anything but a trivial extensions, can actually be quite tricky (so if you’re really “at the very, very beginning of my learning” be prepared for that 😉 )

Robert Möstl 2021-04-23T13:33:31.421100Z

Hello! I can't seem to find where the loading of user.clj is documented. Can someone please point me in the right direction? Background: Wanted to start my ring-based webserver automatically when starting nREPL in Cursive and discovered the existence of user.clj in https://cognitect.com/blog/2013/06/04/clojure-workflow-reloaded by coincidence more or less.

2021-04-23T13:35:26.421800Z

user.clj is loaded if found on the classpath, when you load a file all the code in it gets run

2021-04-23T13:36:02.422700Z

that's really all there is to know (besides of course idioms / cleverness for setting up your dev env by putting fun and useful things in user.clj)

2021-04-23T13:36:48.423600Z

I personally have a ~/user.clj that isn't on class path, and use load-file to source it into a repl, because having to explicitly source it eliminates a source of weirdness from my dev flow

2021-04-23T13:37:32.424200Z

just like any other clojure file, any top level side effects run when the file loads, and any definitions get installed in that file's ns (user, for user.clj, of course)

2021-04-23T13:38:56.425Z

I think of it as clojure's dot file for local dev (and similarly I keep my dot files as empty as possible except when it comes to UI flow tweaks)

Robert Möstl 2021-04-23T13:41:01.425500Z

Thanks.

2021-04-23T13:42:06.427Z

also - don't put a user.clj file in your repo - instead, use config to add its location to that repo's classpath, a user.clj shouldn't end up in your jar / deploy

Robert Möstl 2021-04-23T13:42:09.427100Z

I understood that already, but was looking for more in-depth information to understand the platform as a whole.

2021-04-23T13:42:20.427400Z

there's nothing else to know frankly

2021-04-23T13:42:48.428100Z

it can do what any other clojure file can do when loaded

Robert Möstl 2021-04-23T13:42:52.428200Z

Yeah, but I kind of wonder why it's not mentioned anywhere on official http://clojure.org.

2021-04-23T13:43:32.429Z

probably because it's so simple / supported but not encouraged? but that's mind reading, a clojure core dev should answer that

Robert Möstl 2021-04-23T13:44:47.429500Z

Anyway, I appreciate you answers nonetheless.

2021-04-23T13:48:06.431100Z

one design concern is the conflict between a personal project-agnostic user.clj and a user.clj that provides behavior to one project, arguably the latter is better off being a custom entry point (nothing stops you from having a -main for dev that both launches a web server and an nrepl server)

Basile 2021-04-23T13:51:42.432200Z

Thank you @prnc I’m going to look at Chromex. Cheers @nikola806 – seems like this is more “the clojure way”: understand it properly, then build it youself 🙂

Robert Möstl 2021-04-23T13:51:55.432500Z

Is it a REPL's job to load user.clj? Or is it baked into clojure core?

ghadi 2021-04-23T13:53:14.433Z

clojure core

2021-04-23T13:54:10.434700Z

that has to be my easiest clojure search source yet the only mention of the string in the codebase

ghadi 2021-04-23T13:54:11.434800Z

some of us are not a fan of user.clj -- the presence of it can lead to a surprising initialization process

💯 1
Robert Möstl 2021-04-23T13:55:10.435600Z

There we go, thanks!

2021-04-23T13:55:35.436100Z

yeah I consider checking a user.clj into a repo bad hygiene, similar to putting a .emacs or .vim file in a project

Robert Möstl 2021-04-23T13:56:10.436300Z

Me too.

Robert Möstl 2021-04-23T13:56:52.437400Z

Next logical question would be: How else do I bring nREPL to execute some app init code on startup?

2021-04-23T13:57:15.438100Z

add nrepl as a dev profile lib, have a main that starts it

2021-04-23T13:57:45.438400Z

nearly a one liner

Robert Möstl 2021-04-23T14:05:16.440400Z

"it" being the server? In my case the ring.adapter.jetty.run-jetty call

2021-04-23T14:14:17.441500Z

it being nrepl, the jetty call is a second one liner

2021-04-23T14:16:01.442Z

https://github.com/nrepl/nrepl https://nrepl.org/nrepl/usage/server.html (start-server :port 7888)

2021-04-23T14:16:36.442500Z

that doc also has build tool config for implicitly starting nrepl alongside another task

2021-04-23T14:17:30.442900Z

for safety you might want the ns that starts nrepl to not end up in your build

bnstvn 2021-04-23T14:38:52.447400Z

with tools.cli, can i parse an option with an optional value? so somehow --opt 123 --other and --opt --other should parse to {:opt 123 :other true} and {:opt true :other true} (not to {:opt "--other"} )

Robert Möstl 2021-04-23T15:07:36.447500Z

Ah, thanks. Guess I should have explained my setup better, though. My current noob setup is to start the REPL and within the REPL start the jetty-adapter with my root request handler. If I understand correctly, you suggest to have an ordinary main and start the REPL in there. The reverse somewhat.

Robert Möstl 2021-04-23T15:08:31.447700Z

Btw, I am aware that I should probably not operate my webserver via a REPL in production.

seancorfield 2021-04-23T15:28:31.448800Z

@ban.istvan No. An option either has a value (argument) or it is a boolean toggle. It can’t be both.

🙏 1
piyer 2021-04-23T15:35:22.450700Z

@seancorfield I liked the ddl on the old next.jdbc, I couldn't find ddl docs on the seancorfield/next.jdbc, any pointer? I can also use the old one just for the migration, wondering what is the recommended way.

seancorfield 2021-04-23T15:50:48.451400Z

@munichlinux What DDL in c.j.j do you mean?

seancorfield 2021-04-23T15:51:38.452100Z

You’re just going to call jdbc/execute-one! with a SQL string to do DDL stuff, right?

seancorfield 2021-04-23T15:54:21.454500Z

c.j.j only provided half-hearted CREATE TABLE and DROP TABLE helper functions — you still had to execute that SQL. If you want more of a DSL for building SQL, look at HoneySQL: it has extensive DDL support in v2. See https://cljdoc.org/d/com.github.seancorfield/honeysql/2.0.0-beta2/doc/getting-started/sql-clause-reference#ddl-clauses and https://cljdoc.org/d/com.github.seancorfield/honeysql/2.0.0-beta2/api/honey.sql.helpers for details.

piyer 2021-04-23T16:04:58.455500Z

@seancorfield got it

seancorfield 2021-04-23T16:07:20.456200Z

@munichlinux Feel free to ask any related Qs in #honeysql and/or #sql (I’ll be more likely to see Qs there than here).

piyer 2021-04-23T16:12:34.456300Z

nice, Thank you

seancorfield 2021-04-23T16:13:55.456700Z

And don’t be put off by HoneySQL v2’s Beta status — quite a few folks are already using it in production. We have a mix of v1 and v2 in production ourselves.

piyer 2021-04-23T16:14:38.456900Z

perfect! I am taking all that to production as well.

seancorfield 2021-04-23T16:23:12.457200Z

FWIW, at work we wrote our own ad hoc migration setup, but if I was starting over with that I would use migratus

seancorfield 2021-04-23T16:23:54.457700Z

(and we just use standalone SQL files, with a Clojure script that sucks them in and runs jdbc/execute-one! on each statement)

piyer 2021-04-23T16:27:48.459900Z

Looking at migratus. I was thinking about using ragtime.

Endre Bakken Stovner 2021-04-23T16:28:48.460800Z

I have a function like:

(defn a-fn []
  (let [v1 (compute-v1)
        v2 (compute-v2 v1)
        v3 (compute-v3 v2)
        v4 (compute-v4 v3)]
    v4))
Every computation compute{v1..v4} might fail (indeed - they are expected to often do so). In that case I'd like to show a nice error message (perhaps with more metadata and suggestions for what might have gone wrong) to the user. Is there a nice pattern for doing this? I could have a lot of try/catches but that feels non-functional.

2021-04-23T16:31:53.462400Z

instead of returning just the value return a map of {:error ...} or {:value ...} and write all your compute functions to pass maps that contain :error through unchanged

2021-04-23T16:32:43.463200Z

(the Either monad if you need a name for it)

Endre Bakken Stovner 2021-04-23T16:32:45.463300Z

Feels very functional (monadic-ish). Thanks!

Endre Bakken Stovner 2021-04-23T16:32:53.463500Z

I should have thought of it.

2021-04-23T16:35:01.464700Z

if you can then add things like (defn app [f] (fn [x] (if (contains? x :error) x (f (:value x)))))

2021-04-23T16:35:56.465500Z

or I guess (defn app [f x] (if (contains? x :error) x (f (:value x)))) would be better for the name app

piyer 2021-04-23T16:37:43.466500Z

sweet! migratus supports https://github.com/yogthos/migratus#defining-a-code-based-migration

Endre Bakken Stovner 2021-04-23T16:39:55.468Z

Sometimes the error messages occur in libraries I use. I rely on stuartsierra/dependency which throws an error in case of a circular DAG for example:

(-> (dep/graph)
    (dep/depend :a :a))

;; Execution error (ExceptionInfo) at com.stuartsierra.dependency.MapDependencyGraph/depend (dependency.cljc:89).
;; Circular dependency between :a and :a
What is the best way to collect such errors?

Endre Bakken Stovner 2021-04-23T16:41:05.468100Z

Yes, yes clever! I have played around with Haskell, so I like this solution a lot.

Endre Bakken Stovner 2021-04-23T16:44:04.469900Z

Just have a general catch Exception and put the Exceptions collected into the map I pass around? Also, this feels like it would be a lot of duplication of code. In Python it seems like the kind of thing I'd do with decorators. Should really read up on this.

dpsutton 2021-04-23T16:48:27.470200Z

(defn app [f x] (if (contains? x :error) x (try (f (:value x)) (catch Exception e (assoc x :error (ex-data e))))))

🙏 1
dpsutton 2021-04-23T16:49:42.471600Z

ex-data probably isn't what you want but it's probably a part of what you want to get from any errors

ghadi 2021-04-23T16:49:50.471800Z

@endrebak85 most libraries do not throw exceptions, except to mean 1) "you're holding it wrong" or 2) this is unrecoverable

ghadi 2021-04-23T16:50:45.473400Z

e.g. network disconnect -> that's an exception

ghadi 2021-04-23T16:51:19.474400Z

carefully choose places to catch and turn exceptions into data

Endre Bakken Stovner 2021-04-23T16:53:34.476600Z

I see and agree. I never throw exceptions, but a few of the libraries I use might. Also I like the map of :error and :value approach because then I can add a :warnings-key too.

ghadi 2021-04-23T16:55:41.477Z

cognitect.anomalies is another approach in this space:

ghadi 2021-04-23T16:55:51.477300Z

https://github.com/cognitect-labs/anomalies

sova-soars-the-sora 2021-04-23T17:04:46.478700Z

It's interesting reading the ongoing discussion about error handling. I know Java pretty much demands the use of lots of try/catch blocks, but I don't use them at all in Clojure ...

ghadi 2021-04-23T17:07:23.480600Z

you'll need to use them any time you interact with IO

Endre Bakken Stovner 2021-04-23T17:07:48.481400Z

I'm actually tempted to just have a top level try/catch for now. I'll use a more advanced pattern when I see a need for it.

ghadi 2021-04-23T17:07:51.481500Z

Clojure isn't isolated from having to handle problems coming from disk or network or buggy libs

sova-soars-the-sora 2021-04-23T17:08:29.482200Z

that's a fair point. so mainly for IO

Endre Bakken Stovner 2021-04-23T17:20:15.483400Z

I am getting this error when trying to pprint a vec in my code:

java.lang.ClassCastException: class clojure.lang.PersistentVector cannot be cast to class java.io.Writer (clojure.lang.PersistentVector is in unnamed module of loader 'app'; java.io.Writer is in module java.base of loader 'bootstrap')
When I try in a REPL it works just fine. What might be wrong?

2021-04-23T17:25:06.485300Z

somewhere something has the arguments reversed

🙏 1
Endre Bakken Stovner 2021-04-23T17:25:09.485400Z

The problem was that I was sending two arguments to pprint. (print "a" "b") works, but not pprint.

bastilla 2021-04-23T19:23:53.490500Z

A very basic question: (In Clojurescript) I generate some <input> tags (hiccup), and the first one should be set with {:autofocus true}, the rest without. So how to single out the first element as such. (Very basic if you control the RAM, but in FP not so much.) PS: I loop via (for [x map] ...). Thanks!!

Aron 2021-04-23T19:31:15.491200Z

there are many ways, but probably you want something more idiomatic for generating UI than a for loop

practicalli-john 2021-04-24T10:07:29.022900Z

I've used for quite a lot for streamlining hiccup style code in ClojureScript. So I am curious at so what is more idiomatic than for in Clojure in this case. This has been mentioned to me before, but haven't been offered alternatives. The constraint I am aware of is that for is not composable, but that is not an issue for the hiccup generation code I'm writing.

Aron 2021-04-24T10:17:50.023100Z

could be totally fine in that particular case 🙂 I hope my casual comment doesn't lead to people questioning their working practice, that would be the opposite of what I wish for

Aron 2021-04-24T10:19:59.023300Z

if you share some example, that would be easier to discuss specifically without making bad general judgments :)

practicalli-john 2021-04-24T17:13:34.042900Z

Just curious as someone said something similar a few years ago but didnt elaborate. I like for as its list comprehension is very simple and very powerful for specific cases.

seancorfield 2021-04-23T19:33:11.492700Z

@bastilla perhaps (for [[x auto] (map vector your-data (cons true (repeat false)))] ...) so auto will be true for the first item and false for all the rest.

phronmophobic 2021-04-23T19:33:35.493200Z

I would probably write something like:

(for [[i x] (map-indexed vector xs)
      :let [first? (zero? i)]]
  [:div (merge {:normal-prop "foo"}
               (when first?
                 {:autofocus true}))
   ...])

bastilla 2021-04-23T19:33:46.493300Z

I'd take the "most" idiomatic approach there is. Especially if that info (first element) is then easily accessable. So far I did't see any probs with for.

seancorfield 2021-04-23T19:33:49.493600Z

(but there are all sorts of possible answers)

➕ 1
az 2021-04-23T19:37:34.496600Z

Any ideas why running lein run on a luminus project wouldn’t generate an .nrepl-port file even though nrepl is starting up?

bastilla 2021-04-23T19:45:13.003900Z

I get your approach @seancorfield. And @smith.adriane, I don't know map-indexed yet, but will definitely have a look at it. Meanwhile I came up with this idea:

(doall (for [x map :let [first? (= x (first map))]] ...))
If this is horrendous, pls grab my arm. I'd also like to say (again) how awesome it is to have folks like @seancorfield and all these illuminaries answer to noobs like me. Again, this community is really supportive. ok, 'nuff lickspittling, just saying.

Oliver 2021-04-23T19:47:25.005800Z

How can I filter on a nested data structure, eg. I want to filter out all even numbers in [{:a [0 1 2 3 4]} {:b [5 7 8 9]}] => [{:a [1 3]} {:b [5 7 9]}] ?

phronmophobic 2021-04-23T19:50:59.006Z

I would advise against checking (= x (first map)) since it may return true if map contains duplicates.

seancorfield 2021-04-23T19:51:35.006200Z

Instead of (doall (for [x map] (do-stuff :to x))) you could do (mapv #(do-stuff :to %) map) and then you’re guaranteed a vector result and you could just (assoc-in result [0 :autofocus] true) — or whatever is the appropriate path into that first element.

phronmophobic 2021-04-23T19:51:58.006400Z

I guess it won't have duplicates in the case that map is a hashmap, but if you reuse the same code elsewhere, it might get you into trouble

seancorfield 2021-04-23T19:52:32.006700Z

Given it’s using for, I wouldn’t expect it to be a hash map but maybe…?

phronmophobic 2021-04-23T19:53:04.006900Z

right, but it is called map 🤷

2021-04-23T19:56:05.007100Z

the easiest way to do this is via (mapv #(into {} (map (fn [[k v]] [k (filterv odd? v)]) %)) xs) , if you need to do this for arbitrarily nested lists then you might need to use something from clojure.walk

1
Aron 2021-04-23T19:57:42.007300Z

(let [arr ["a", "b", "c"]] (vec (concat [(clojure.string/upper-case (first arr))] (rest arr))))

bastilla 2021-04-23T20:00:16.007700Z

Yes, it's a map. Thanks a lot (and @seancorfield @smith.adriane @ashnur`)`. I have plenty of mind food again.

seancorfield 2021-04-23T20:02:57.008Z

@bastilla You know that hash maps are unordered, so you’ll get your fields in a random order, depending on how many items are in the map and what their keys are?

seancorfield 2021-04-23T20:03:56.008200Z

So there’s really no sense of “first” in a hash map — it’s just a random element that happens to appear in the first slot when the hash map is converted to a sequence…

bastilla 2021-04-23T20:04:23.008400Z

Yes, I know. Idea was: first should get the same elem as for (at first take).

bastilla 2021-04-23T20:05:14.008600Z

But I guess this isn't guarantueed.

seancorfield 2021-04-23T20:05:16.008800Z

True, it does — because seq is repeatable for the same hash map. But getting your HTML fields in a random order seems… weird…

bastilla 2021-04-23T20:06:46.009Z

Concerning HTML forms, you're right. I was just curious and had this question at other occasions, too.

Lu 2021-04-23T20:55:15.009300Z

I’d rather do this purely for readability purposes

(for [entry data
      [k v] entry]
  {k (filter odd? v)})

1
Darrell 2021-04-23T22:48:07.010600Z

Is there an idiomatic way of assigning a conditional value? For example; if foo is empty then assign x to “bar” else assign x to “baz”

2021-04-23T22:48:40.011100Z

(let [x (if foo "bar" "baz")] ...)

Darrell 2021-04-23T22:49:55.011800Z

Hm, I could swear I tried that.

Darrell 2021-04-23T22:53:09.013500Z

Ah, I see what I did wrong. Thanks @hiredman!