This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-07-09
Channels
Hi, I'm writing some tests for my endpoints. Two questions: 1. I'm using VS with calva. Is there any integrated test runner I can use? 2. How do I choose a test db when running tests (So db operations happen on test db not the dev one)?
https://calva.io/test-runner/ Tests are normally ran at the REPL. So these commands will have your tests run inside your active REPL, meaning you must be jack-in-ed
As for a test db, that kind of depends. Normally unit tests should tests only the units, with the db mocked. Meaning you don't really need a db at all. If you're doing integ tests. and you have some test db separate from your dev db, you just want to point to a different db url. Normally that's done through some kind of component library like: https://github.com/stuartsierra/component https://github.com/weavejester/integrant https://github.com/tolitius/mount Those are the most popular. They let you do dependency injection, so you can configure a different system to be injected during tests versus dev versus prod.
Ok, how do I mock the db and use a mocked db ? For example, here's an endpoint:
(defn add-category-handler [request]
(let [category-data (:body request)
result (db/add-category category-data)] ; call your db function to add a category
{:status 200
:headers {"Content-Type" "application/json"}
:body {:message "Category added successfully!" :category result}}))
db/add-category category-data
:
(defn add-category [category]
(sql/insert! db-spec :categories category))
db-spec
here is my config for dev db.
How do I use a mocked db here? Is there any sample code I can look at?with-redef works fine for that, as long as you remember that it is global, so make sure you don't have tests running in parallel, or using async code. There are some nicer libs that adds some features like let you specify the return for some argument, or check how many times it was called, with what value, etc., such as: https://github.com/alexanderjamesking/spy https://github.com/nubank/mockfn
Inserting a row into a table with next.jdbc results in a map like following:
#:categories{:id 4, :name "Test Category", :description "Test Description"}
Is it a good idea to pass this as-is as result from a route?
response body looks like this:
{
"message": "Category added successfully!",
"category": {
"categories/id": 4,
"categories/name": "Test Category",
"categories/description": "Some category"
}
}
Should I "un-namespace" keys beforehand?If I personally was consuming that in some other code most likely the frontend I would curse you for making me use string key names. Also the “categories” namespace is redundant since it is already nested under “category”
//If I personally was consuming that in some other code most likely the frontend I would curse you for making me use string key names.// What do you mean by this? Unclear to me
Hi everyone, happy Sunday! 🙂
Question: When serving a page from the backend, how can I go about including main.js
in particular and resources in general?
Context:
I'm working through the official Sente example available here:
https://github.com/ptaoussanis/sente/tree/master/example-project
It was implemented using Leiningen. I'm trying to work through it with Clojure CLI and Shadow CLJS instead. (The example also uses http-kit and Compojure, whereas I'm using Aleph and Reitit.)
To connect the server and client, main.js
has to be included in the page served by the server. From the server side in the example, this is done like https://github.com/ptaoussanis/sente/blob/a9f3ccd5c9c229bfaa6dc0de897e8d2c7f2ff088/example-project/src/example/server.clj#L82:
;; src/example/server.clj (from the example)
(defn landing-pg-handler [ring-req]
(hiccup/html
... ;; elided for brevity
[:script {:src "main.js"}] ; Include our cljs target
))
I've followed the example quite closely, changing the JS directory slightly:
;; src/example/server.clj (mine)
(defn landing-page-handler
[ring-req]
{:status 200
:headers {"content-type" "text/html"}
:body
(hiccup/html
...
[:script {:src "./js/main.js"}])})
In shadow-cljs.edn
, I've specified the output directory as resources/js/
;; shadow-cljs.edn
{
...
:builds {:app {:target :browser
:output-dir "resources/js/"
:asset-path "/js"
:modules {:main {:init-fn example.client/init}}}}
...
}
and in deps.edn
included resources
path:
;; deps.edn
{
...
:paths ["src", "resources"]
...
}
The landing serves fine and resources/js/main.js
exists (generated by the Shadow CJS watcher). However, upon opening the browser console I can see that main.js
is not found. The message in its entirety:
GET net::ERR_ABORTED 404 (Not Found)
How can I go about including main.js
in particular and resources in general?
Thanks for your time!Or I guess it's reitit in fact: https://cljdoc.org/d/metosin/reitit/0.7.0-alpha5/doc/ring/static-resources
Or.. you can also do it with ring directly I think: https://github.com/ring-clojure/ring/wiki/Static-Resources
Ok... So looking at the sente example. You can see that it uses ring/ring-defaults wrap-defaults for Middleware. This does a lot for you by convention. If you look at the code: https://github.com/ring-clojure/ring-defaults/blob/master/src/ring/middleware/defaults.clj#L107 You see that it does: (wrap-multi wrap-resource (get-in config [:static :resources] false)) (wrap-multi wrap-file (get-in config [:static :files] false)) That's what allows static files to be served by the Ring server. I believe it uses some default paths, and maybe since you use a different path, that's not served by default
The default it's using is here: https://github.com/ring-clojure/ring-defaults/blob/master/src/ring/middleware/defaults.clj#L49
You can see this is where the example outputs the compiled Clojurescript JS: https://github.com/ptaoussanis/sente/blob/a9f3ccd5c9c229bfaa6dc0de897e8d2c7f2ff088/example-project/project.clj#L52
TL;DR
Change you shadowcljs output directory to: resources/public/js
Because Ring is configured by default to serve static assets from the resources/public
folder, other assets inside resources
directly, that are not under public
will not get served.
Thank you @U0K064KQV! Did the trick. Impressive sleuthing on show there.
What's the problem with the following?
(defn render-calendar-month
"Render months on top of calendar"
[]
(str (element "div"
:class "calendar-month-row" ; TODO switch to named args... wasn't working.
:content (str (clojure.string/join (mapv (fn [x] (element "div" "calendar-month" "" x)) monthsName ) ))
:style nil
)))
Is element
from clojure.data.xml or something else? What is the value of monthsName
? And maybe most importantly, what leads you to believe there's any problem with it? Are you getting an error message? Are you getting a different return value than expected? What are you expecting, and what is it actually returning?
Sorry, had a outtage right when I sent this by mistake... element is my own:
(defn element
([name class style content]
(str "\n<" name (when class (str " class=\"" class "\" "))
(when style (str "style=\"" style "\" "))
">\n "
(when content (clojure.string/replace content "\n" "\n "))
"\n</" name ">"))
([name class style]
(element name class style nil)
)
([name [& {:keys [class style content] :or {class nil style nil content nil }}]]
(element name class style content)
)
)
That's the error:
Wrong number of args (7) passed to cljs-playground.core/element
--------------------------------------------------------------------------------
64 | :class "calendar-month-row" ; TODO switch to named args... wasn't working.
65 | :content (str (clojure.string/join (mapv (fn [x] (element "div" "calendar-month" "" x)) monthsName ) ))
66 | :style nil
67 | )))
--------------------------------------------------------------------------------
I believe the :or named argument is useless since they are nil anyway? I tried it because of the arity error in case
monthsName is just a list of string and that part was working fine before I tried to change for named args
So, the short version here is that the "named args" don't work exactly as has been written.
The arity of element
with its vector of [& :keys ...
is expecting two arguments, name
and a sequence that will be partitioned into the keyword args. This is by virtue of the fact that the & :keys
part is in a vector. If you take away the vector, then the function will fail to compile because it's not necessarily clear which arity to call
to demonstrate this, in the render function, if you put the 'named args' into a vector, then the function will compile and produce output (whether that output is entirely correct is not known to me, but it seems to do more or less the right thing)
if we think about the ways that the element function could get called if we took & :keys
out of the vector, then (element "myname" :class "cheese")
becomes ambiguous - we could be trying to pass keyword ('named') args, or we could be trying to call the 3-arity of [name class style]
- I believe this is why the & :keys
fails to compile when we take it out of the vector
to address the ambiguity, we could either add the vector into the element
call from the render function (as leave the definition in render as it is), or we could pass that map as a map and do an associative destructuring (without the vector or the &
) on the second argument
another alternative would be to say "creating an element from keyword args gets a slightly different name", and make a new function that takes the kw args and calls element using the values passed in
So if I had the same function but without other arities, just the name [& keys...] it would work?
well, the fact that it's in a vector is an important part here... as written, it works, but those keys need to be in a vector, e.g. (element "myname [:class "something])
- if you remove the vector in [& :keys ...
and removed the other arities (or made this a definition of a different function without the other arities), then (element "myname :class "cheese")
(note that there's no vector there) would work
Oh I see! Thanks a lot
to avoid a huge code block, I threw together <https://pastebin.com/WUp0JqaH> with three different variants that all produce the same output - seeing the options might obviate the minor differences
Hi Clojurians. Im wondering if anyone can help shed light on why my 'opener' function fails on line 6? This is a cutdown example of something that has been puzzling me for a bit. I can quite happily interop java on the methods of the 'line' object if I call them directly in the immediate scope that I created line. But if I instead try to perform the same java interop on the object inside a function to which it gets passed, the call fails with a reflective exception
(def sample-rate (* 16 1024))
(def audio-format (javax.sound.sampled.AudioFormat. sample-rate 8 1 true true))
(let [line (javax.sound.sampled.AudioSystem/getSourceDataLine audio-format)
_ (.open line audio-format sample-rate)
opener (fn [ln] (.open ln audio-format sample-rate))]
(opener line)
(.close line)
)
No matching method open found taking 2 args for class com.sun.media.sound.DirectAudioDevice$DirectSDL
The following seems to work:
(def sample-rate (* 16 1024))
(def audio-format (javax.sound.sampled.AudioFormat. sample-rate 8 1 true true))
(let [line (javax.sound.sampled.AudioSystem/getSourceDataLine audio-format)
;; _ (.open line audio-format sample-rate)
opener (fn [^javax.sound.sampled.SourceDataLine ln] (.open ln audio-format sample-rate))]
(opener line)
(.close line))
You can maybe use clojure.reflect/reflect
to try and figure out why it's not finding a matching method, but hinting the Class seems to fix it.
Thanks @U7RJTCH6J . That worked for me too. Definitely unblocked now. i still hope to understand how its getting handled, but can at least move past this now 👍
I need a second pair of eyes here to help understand what I am doing wrong 😂 this function is supposed to rotate a line segment by a given radian angle around the line segment's center. Would love tips on how to make it more readable as well:
(defn rotate
"Rotates a line segment around the center by theta"
[[start end] theta]
(let [[[x1 y1] [x2 y2]] (if (left? start end) [start end] [end start])
[center-x center-y] [(/ (- x2 x1) 2) (/ (- y2 y1) 2)]
diff-y-end (- y2 center-y)
diff-x-end (- x2 center-x)
diff-y-start (- y1 center-y)
diff-x-start (- x1 center-x)
sin-theta (Math/sin theta)
cos-theta (Math/cos theta)
x-end-normalized (- (* cos-theta diff-x-end) (* sin-theta diff-y-end))
y-end-normalized (+ (* sin-theta diff-x-end) (* cos-theta diff-y-end))
x-start-normalized (- (* cos-theta diff-x-start) (* sin-theta diff-y-start))
y-start-normalized (+ (* sin-theta diff-x-start) (* cos-theta diff-y-start))]
[[(+ x-start-normalized center-x) (+ y-start-normalized center-y)]
[(+ x-end-normalized center-x) (+ y-end-normalized center-y)]]))
So given the line segment [[0 1] [1 0]]
and the angle -PI/4
, I would expect this line to be flat with a length of sqrt(2)
, so something like [[0 0.5] [1.4 0.5]]
. Instead this is the result
(comment (let [line [[0 1] [1 0]]]
(rotate line (/ Math/PI -4)))) => [[0.7071067811865475 1.4142135623730951] [0.7071067811865475 5.551115123125783E-17]]
Since the x point are basically equal, this line is vertical, which isn't what I expect. Am I doing something wrong here?https://github.com/plumatic/plumbing maybe something to look at (the graph fn) for readability
if you are interested in this code being fast, https://neanderthal.uncomplicate.org/
Thanks, bookmarked! Currently running in cljs but at some point will likely move to JVM so this will be useful
You can use also fastmath.vector
and in terms of vector operations it will be:
(require '[fastmath.vector :as v])
(defn translate-and-rotate
[v offset theta]
(-> (v/sub v offset)
(v/rotate theta)
(v/add offset)))
(defn rotate-segment
[[v1 v2] theta]
(let [offset (-> (v/sub v2 v1)
(v/div 2)
(v/add v1))]
[(translate-and-rotate v1 offset theta)
(translate-and-rotate v2 offset theta)]))
(rotate-segment [(v/vec2 1 0) (v/vec2 0 1)] m/-QUARTER_PI)
;; => [[0.5 -0.20710678118654746] [0.49999999999999994 1.2071067811865475]]
Rotation is counterclockwise for positive angles, and clockwise for negative angles.