Fork me on GitHub
#beginners
<
2022-07-09
>
zakkor13:07:12

I'm trying to walk/update an xml/html-like tree structure, which looks like this:

{:tag :button,
 :attrs {},
 :content
 ["Text content" {:tag :span, :attrs {}, :content ["Text content"]}]}
I tried defining my own seq for it using tree-seq, like this:
(defn tmpl-seq [tp] (tree-seq map? :content tp))
But when I walk it using the clojure walker functions, I get unexpected results:
(def tpseq (tmpl-seq @root-tp))
(pprint (prewalk (fn [x] x) tpseq)) ;; expecting to get the same tree structure back unchanged
=> ({:tag :button,
  :attrs {},
  :content
  ["Text content" {:tag :span, :attrs {}, :content ["Text content"]}]}
 "Text content"
 {:tag :span, :attrs {}, :content ["Text content"]} 
 "Text content")

Bob B14:07:00

the prewalk does return the same structure: (= (prewalk (fn [x] x) (tmpl-seq a)) (tmpl-seq a)) ; true but the tree-seq creates a seq of the various tree elements - try the tmpl-seq call on its own - I think that might be the unexpected piece

zakkor14:07:32

Oh right, I guess that's not how I expected/wanted the seq to work. I think I want it to return nodes only

zakkor14:07:37

I'm not sure how to solve it though. Basically I don't really care what the seq looks like, I just want to be able to do a nested update in the tree, and get back an updated tree root

teodorlu14:07:58

tree-seq flattens a tree to a flat sequence. Sounds like that's not what you want? I'd try a straight prewalk or postwalk.

zakkor14:07:31

Huh, yeah, that magically works. I wasn't sure how postwalk would be able to tell how to walk through my special structure, but I guess it just navigates through everything so it doesn't really need to know what key the children are located at

1
zakkor14:07:58

Thanks!

👍 1
teodorlu16:07:41

Prewalk and postwalk have native support for associative collections and sequences. If you just have nested maps, it knows what to do :)

teodorlu16:07:57

I think this line, specifically (into (empty form) ,,,) is what makes maps come out :) https://github.com/clojure/clojure/blob/master/src/clj/clojure/walk.clj#L50

HT18:07:47

I am trying to run this file tool/db/core_test.clj with lein test, and for some reason the count test fails It seems like create-user is not running, but I checked that it is because when I run it by removing {:rollback-only true}] then I was able to see in my DB that a row was created Is this is an issue of the create-user not having fully finished commiting the row to the db by the time the get-users runs? I'm using postgres though so I should be able to read the rows that were just created in the middle of the transaction. Not sure why it's not working.

(ns tool
.db.core-test
  (:require
   [tool
.db.core :refer [*db*] :as db]
   [java-time.pre-java8]
   [luminus-migrations.core :as migrations]
   [clojure.test :refer :all]
   [next.jdbc :as jdbc]
   [tool
.config :refer [env]]
   [mount.core :as mount]))

(use-fixtures
  :once
  (fn [f]
    (mount/start
     #'tool
.config/env
     #'tool
.db.core/*db*)
    (migrations/migrate ["migrate"] (select-keys env [:database-url]))
    (f)))

(deftest test-users
  (jdbc/with-transaction [t-conn *db* {:rollback-only true}]
  ;; (jdbc/with-transaction [t-conn *db*]
    (println (select-keys env [:database-url]))
    (is (= 1 (db/create-user!
              t-conn
              {
               :first_name "Sam"
               :last_name  "Smith"
               :email      ""
               :role "user"
               :password       "pass"}
              {:connection t-conn}
              )))
    (is (= 1
           (count (db/get-users t-conn )))) ;; this test fails
    ))

👀 1
Bob B18:07:28

is it possible that db/create-user! does something that manipulates the connection in such a way that the transaction gets closed (and given the rollback-only option, rolls back the trx)? Have you tried replacing the create-user with a more primitive jdbc/execute call to help differentiate?

HT19:07:20

@U013JFLRFS8 This is my resources/sql/queires.sql file. It's just a simple insert.

-- :name create-user! :! :n
-- :doc creates a new user record
INSERT INTO users
(first_name, last_name, email, password, role, created_at, updated_at, last_login)
VALUES (:first_name, :last_name, :email, :password, :role, now(), now(), now())

-- :name get-users :? :*
-- :doc retrieves all user records
SELECT * FROM users

-- :name get-user :? :1
-- :doc retrieves a user record given the id
SELECT * FROM users
WHERE id = :id
This is my first time using clojure so do you have some code or more detail on how I would use a "jdbc/execute" call?

HT19:07:43

Ah ok something like this I assume

(jdbc/with-transaction [t-conn *db* {:rollback-only true}]

    (jdbc/execute! t-conn ["
INSERT INTO users
(first_name, last_name, email, password, role, created_at, updated_at, last_login)
VALUES ('first_name', 'last_name', 'email', 'password', 'role', now(), now(), now())
                           "])

    (is (= 1
           (count (db/get-users t-conn ))))
I ran this, but same result where it sees 0 rows

Bob B19:07:00

in that case, I'd speculate that it might be an auto-commit thing, but I'm not too familiar with how auto-commit and rollback only play together in the context of a transaction - I'll see if I can spin up a repro, but it'll probably take me a little while (or someone smarter than me will come along with the right answer)

Bob B20:07:26

what is *db*? I'm unable to re-create the situation you describe. Admittedly, I'm not using a test fixture or mount, but I'm always getting the row back from within the trx, and it gets rolled back and isn't there afterwards

kennytilton20:07:15

Pardon a dummy jumping in but I googled a bit and it seems that is what :rollback-only does, ie, execute the command and then rollback immediately, even on success. This is next.jdbc, but it sounds relevant. Look for ":rollback-only". https://cljdoc.org/d/seancorfield/next.jdbc/1.2.659/doc/getting-started/transactions hth

HT21:07:08

@U013JFLRFS8 here's my repo - should be able to reproduce it from this if you have a postgres db running locallly https://github.com/himat/del

HT21:07:43

@U0PUGPSFR I saw that, but it should rollback after the entire transaction right? Not in the middle. I have both create-user and get-users within the one overall transaction The line after that in the docs also agrees: "The latter can be particularly useful in tests, to run a series of SQL operations during a test and then roll them all back at the end."

rolt01:07:47

i had a look at the code, apparently you need to do (db/get-users t-conn {}) so that it uses properly the connection pool. I think it might be conman, because I don't think i encountered this issue with hugsql. It's probably due to their automatic detection of the db component, so I guess (db/get-users t-conn) was converted to (db/get-users *db* t-conn) which missed the current transaction's writes. Btw you don't need that 3rd arg in db/create-user!

Bob B01:07:51

it does seem to be because of a slightly different behavior in conman - I switched to conman's with-transction and the test now passes

rolt01:07:00

yes it should work too

rolt01:07:01

i feel like conman should check if the first argument satisfies the connectable protocol or something like that, instead of only relying on arity