Fork me on GitHub
#beginners
<
2022-12-15
>
piyer02:12:16

Java time is hard to navigate; I am using https://cljdoc.org/d/tick/tick/0.5.0-RC5/doc/readme, how do I mutate the date? I am looking to update the date.

phronmophobic03:12:20

It looks like tick is based on java time. I think just about everything in java time is immutable, so you would produce a new value rather than mutate an existing date.

Lennart Buit06:12:41

Java time becomes easier if you understand the relation between LocalDateTime <=> OffsetDateTime <=> ZonedDateTime and Instant

πŸ‘ 1
Tema Nomad08:12:51

What is must-have DB (postgresql) library to use in any production commercial project? What do you use in your real projects? Is it just org.clojure/java.jdbc or something with higher level of DSL?

practicalli-johnny11:12:53

https://github.com/seancorfield/next-jdbc provides database connections via a ranges of database drivers and functions for SQL queries to the database. Keys can also be automatically converted to/from kebab-case

practicalli-johnny11:12:03

https://github.com/seancorfield/honeysql provides a way to define database queries in Clojure rather than SQL and can be used with next.jdbc for database connection

practicalli-johnny11:12:46

The documentation for these projects are very detailed and worth reading. https://practical.li/clojure-web-services/relational-databases-and-sql/ also covers examples of practical application of next.jdbc which have been used in numerous commercial production projects

πŸ‘ 2
phill10:12:40

If you look at the options and waver, I'll just say that (resultset-seq) in clojure.core is a pretty good 85% solution, in one fell swoop lifting the most awful burdens of JDBC. If you must use SQL at all, that one function pays the price of admission to Clojure.

Tema Nomad12:12:40

What do you mean? Just use resultset-seq instead of result-set/as-unqualified-lower-maps ? Or what? Sorry dont understand your message πŸ™‚

phill19:12:31

Just pointing out that the JDBC situation in Clojure is quite different from Java. In Java, JDBC is so hard to use that Hibernate seemed reasonable and Spring on top of that and your question about a must-have library could easily be answered with "Hibernate and Spring and everything in the kitchen sink." But Clojure showed that the hardship was Java's fault, not JDBC's. In Clojure, you might not need any SQL library at all. JDBC is a breeze. Java interop to prepare and run statements, plus the resultset-seq function in clojure.core, might be sufficient. The libraries add various value and elevate the tone, but depending on what you need beyond your own automation of JDBC, it's your choice.

dumrat08:12:27

I guess next.jdbc is the one now

🧡 3
Tema Nomad09:12:54

do you use it in real web project? Isn't it too low level for that?

dumrat09:12:02

I don't use it in a real web project - nothing big. Not exactly sure what you mean by too low level though. Clojure is quite against ORMs and stuff so you'd have to explain. I just use it in tandem with honeysql in various tools.

πŸ‘ 2
pavlosmelissinos10:12:34

For building less dynamic queries hugsql is quite good as well

πŸ‘ 1
seancorfield17:12:58

@U04BRV8JQKE We use next.jdbc heavily in production, powering 40 online dating sites with millions of users and a huge MySQL database. We also use HoneySQL to construct complex queries when we need to compose SQL fragments conditionally. We still use clojure.java.jdbc as well and the original 1.x of HoneySQL since our codebase in over a decade old in places (134,000 lines of code now).

πŸ™ 1
Tema Nomad17:12:05

@U04V70XH6 yeah, I see. I am already adding next.jdbs + honeysql to my pet project. They are very simple to understand, thank you! Specially honeysql DSL for complex queries

Tema Nomad04:12:04

Is there a case when "symbol" honeysql helpers are better than "keyword"? Or I can write any complex queries via "keyword" helpers like :select ?

seancorfield04:12:37

It's personal preference, with a couple of very minor exceptions (that you hardly ever need to worry about).

seancorfield04:12:16

If you look in the tests (and docs), you'll see examples of both, but most are keyword style.

πŸ‘ 1
seancorfield04:12:44

Folks who like datalog style queries will often prefer the symbol forms.

Tema Nomad04:12:52

I love clojure homoiconic so want to use only keywords

seancorfield04:12:34

If you don't need symbols evaluated in a query, a quoted symbol form is less typing 😁

Tema Nomad04:12:01

What do you mean?

seancorfield04:12:13

'(select * from table where (= col ?param))

seancorfield04:12:40

[:select :* :from :table :where [:= :col :?param]]

seancorfield04:12:29

Typed on my phone! That was work!

πŸš€ 1
Tema Nomad04:12:45

But to get a habbit and practice I will write all my queries via keywords for now

Tema Nomad04:12:51

I think it makes sense

Tema Nomad05:12:27

Am I right I cant create database via honeysql ? So I just need to use plain-text SQL via next.jdbc ?

Tema Nomad05:12:57

I did not found any :create-database SQL clause in honeysql docs

dumrat05:12:41

@U04BRV8JQKE you don’t need honeysql for table creation:

Tema Nomad05:12:48

@UC1DTFY1G nice example, thanks. What is about database creation? Then I need to create it not from clojure layer at all? So I need to create it from system layer (docker)

Tema Nomad05:12:38

also I probably need to create at least 2 databases: for dev and for tests

dumrat05:12:39

Im not sure I want to manage dbs through Clojure.

Tema Nomad05:12:26

Yeah I think its better to manage dbs via system tools

seancorfield05:12:09

(jdbc/execute! ds ["create database if not exists foo"]) -- it's a pretty simple SQL command so you don't need HoneySQL for that really.

seancorfield05:12:28

No one has asked for that to be added to HoneySQL yet πŸ™‚

Tema Nomad05:12:06

I think its not required there ) Plain text is enough

seancorfield05:12:34

At work, we use plain .sql files for all our DB migrations. We read them in and run them with next.jdbc.

Tema Nomad05:12:03

I just thinking what is better, to manage databases from docker-compose file (just add another section for additional database) or from clojure

Tema Nomad05:12:54

> plain .sql files for all our DB migrations so there are database creation AND all tables creation code?

seancorfield05:12:10

It's all just DDL.

Tema Nomad05:12:08

Do you mean just CREATE/ALTER/DROP queries?

seancorfield05:12:10

At this point we have about 950 .sql files representing the "migration" up from a completely empty Percona instance started with Docker to four full test databases.

seancorfield05:12:32

Yes. All the DDL is in .sql files that we read and execute.

Tema Nomad05:12:43

ah so you separate it to many .sql files not just 1

seancorfield05:12:38

We do automated SQL migrations to QA and production as well, the same way. We have a table in the DB containing the highest migration applied and then automatically apply any missing migrations as part of our automated deployment.

Tema Nomad05:12:24

looks very relative to rails migration way where I come from here πŸ™‚

seancorfield05:12:21

Libraries like Migratus help automate this. We built our own migration system many years ago.

πŸ‘ 1
seancorfield05:12:39

We have 8 basic DB setup .sql files, 3 initial creation .sql files, and currently 924 individual migration files to modify the schemas etc.

Tema Nomad05:12:17

what is the difference between basic and initial files?

seancorfield05:12:13

The basic files create the four databases and populate our geolocation DB (one of the four). Those are only run on a "cold start" (in CI and occasionally in dev). The initial creation files 1) drop all the tables, 2) create the original set of tables we started the project with (our MVP schema), and set up the dblevel table (to level 0).

seancorfield05:12:48

So we have cold-start and database-setup as two tasks in our build.clj file.

Tema Nomad05:12:12

dblevel is the migration level?

seancorfield05:12:30

We chose a simple increasing number. And we also have the concept of dev-only migrations (which are applied in dev/CI but not QA or production) which are used for loading test data -- but are otherwise just part of the regular sequence of migrations... 00001_metadata.sql, 00002dev_initial_test_data.sql, 00004_cheater_create.sql, 00005_bannedIP_add_admin.sql etc up to 00923_bugfix_WS_14085_covalence_source_too_long.sql, and 00924_feature_WS_14075_admin_filter_skywriting_by_platform.sql -- during the (decade-plus) life of the project we switched to Jira and now use branch names for SQL migrations -- via a next-migration task in our build.clj file πŸ™‚

πŸ‘ 1
seancorfield05:12:02

Hopefully we'll never hit 99,999 migrations πŸ™‚

seancorfield05:12:01

(but, yes, we started with ticket 1 and we're up to about 14,140 now)

seancorfield06:12:28

I blogged about the first ten tickets we created, when we got started on this project back in 2009: https://corfield.org/blog/2014/06/03/getting-started/

popeye14:12:16

I have a code below for future which executing its bosy again and again on calling the function, But the main definition of future is it catch the value, How to write it in function?

(def x 0)
(defn increment-number []
  (let [future-var (future
                     (loop [y x]
                       (Thread/sleep 1000)
                       (println "---y---" y)
                       (if (> y 3)
                         y
                         (recur (inc y)))))]
    @future-var
    [x @future-var]))

(increment-number)

dumrat14:12:34

(defn increment-number []
  (let [future-var
        (future
          (loop [y 0]
            (Thread/sleep 1000)
            (println "---y---" y)
            (if (> y 3)
              y
              (recur (inc y)))))]
    @future-var))

(increment-number)
?

dumrat14:12:01

I don't see the point of the promise or x

dumrat14:12:07

What's your aim here? because x will always be 0

popeye14:12:47

why (println "---y---" y) is executing again and again, how to avoid it ?

popeye14:12:09

basically future-var is not catching its value

dumrat15:12:36

So I assume you mean on each call of (increment-number) you see the print statement right? This is because you are creating a new future in each call inside the function. If you wanted to get the cached value, you can do something like this (simplified):

(defn increment-number [] 
  (future
    (loop [y 0]
      (Thread/sleep 1000)
      (println "---y---" y)
      (if (> y 3)
        y
        (recur (inc y))))))

(let [f (increment-number)]
  [@f @f @f])

dumrat15:12:05

You should see printlns happening only for the first @f

dumrat15:12:20

If you run (increment-number) again, it will print again because you are creating a new future.

popeye15:12:32

@UC1DTFY1G Promise also catch the value right? How it will be helpful in real world scenario?

dumrat15:12:56

I haven't used promise much so if I'm wrong someone can correct me... A future is a computation that starts immediately on a separate thread after the future is created. If you try to deref the future before it's completed, it will block. so:

(def f (future (Thread/sleep 5000) 42))

@f ;Will return 42 after 5 secs
A promise is a promise that a value will be deliver ed to the promise at some time. Difference from future is it does not specify how that will be done:
(def p (promise))

@p
This will block indefinitely as no one delivers on the promise. However, we can deliver in the promise from somewhere else:
(def p (promise))

(future
  (Thread/sleep 10000)
  (deliver p 42))

@p ;Will block for 10 secs then return 42
So, I use future when you want to do something in a separate thread. I haven't used promises yet but I can imagine waiting for some event to happen and deliver on any promises available when that happens.

popeye15:12:36

future function can have side effects right ?

popeye16:12:21

have you used deliver ?

dumrat16:12:50

No, not outside these toy examples. I think deliver is only used in tandem with promise

Akhil Daphara18:12:36

Is there a way to dynamically name a variable? I have the following code

(thread-pool-bulkhead/defbulkhead symbol-name)
defbulkhead is a macro which creates a var in the current namespace with the name "symbol-name" (check docs here - https://github.com/ylgrgyq/resilience-for-clojure/blob/0.2.7/src/resilience/thread_pool_bulkhead.clj#L137) I need that symbol-name to be dynamic in nature. I have this for now,
(eval `(thread-pool-bulkhead/defbulkhead ~(symbol symbol-name)))
but I'm wondering if there's a better way to do it.

valerauko02:12:40

> I need that symbol-name to be dynamic in nature. why?

Akhil Daphara16:12:35

It's just my use-case. I am giving user the ability to create bulkheads dynamically and fetch the correct one when needed

emccue23:12:15

invert your problem a bit - ignore the macro

emccue23:12:03

how can you def anything dynamically is the question to answer

Patrick Winter19:12:05

I am currently learning about spec and why qualified keys are a good idea. How does one deal with keysets which you have no direct control over (e.g. a body of a ring request)? Is there an idiomatic way to wrap an existing map in a namespace so it can be validated by a spec?

Christian Packard19:12:21

another option could be to use (s/keys :req-un […] for handling the unqualified keys directly (https://clojure.org/guides/spec#_entity_maps section of the Spec guide has examples)

βž• 1
dvingo20:12:31

I got bitten pretty bad by this (using unqualified keys) in my days of learning spec. I left some notes here https://github.com/clojure/clojure-site/issues/517 (which still haven't been reflected in the guide) - Since then I have adopted malli (https://github.com/metosin/malli) which solves this and many other design flaws of spec (the API is data+fns instead of macros being a huge one). I recommend taking a look at at it.

dvingo20:12:38

:req-un suffers from the flaw that your specs still need to be namespaced keywords, even though in the map they are not. This can lead to painful collisions of those fully qualified keywords later on, especially in a large codebase, due to spec using a global registry

Christian Packard16:12:02

malli looks interesting, thanks for the tip πŸ‘€

Patrick Winter18:12:53

@U051V5LLP thanks, I will give it a look πŸ‘€