This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-02-12
Channels
- # adventofcode (1)
- # announcements (6)
- # babashka (28)
- # beginners (75)
- # calva (8)
- # clj-commons (4)
- # clojure (39)
- # clojure-austin (5)
- # clojure-europe (7)
- # clojurescript (14)
- # clr (3)
- # datalevin (2)
- # fulcro (7)
- # funcool (1)
- # graphql (4)
- # helix (3)
- # hyperfiddle (5)
- # joyride (15)
- # malli (1)
- # missionary (16)
- # practicalli (1)
- # releases (1)
- # reveal (2)
- # tools-deps (14)
- # vim (9)
I have worked for a while with shadow-cljs and I enjoy the watch workflow. Now I'm looking into Clojure, lacinia and pedestal. I wonder how I could setup the same workflow. Particularly when a file is changed I want a stop function followed by compiler and update and then start function again. Is this possible with clj and deps.edn?
Including the web application (typically the router function) as a reference means that the router function or the handler functions it calls can be evaluated to pick up changes without restarting the application server (e.g. jetty, httpkit, etc)
(app-server/run-server #'app {:port http-port})
Or the application server can be restarted ( using either an atom based reference, mount, integrant, etc.), typically providing start / stop / restart functions
The browser showing a web page from the system would have to be manually refreshed, unless some library or function was added to automatically do thisTo avoid restarting the repl when adding dependencies, add-libs can be used to 'hotload' one or more libraries https://practical.li/clojure/clojure-cli/projects/hotload-in-project/
thank you!
Sorry, I was a little to eager. At the moment I'm using a user-namespace where I start the server when I start testing. Then I change my code, stop it via the user namespace, refresh changed namespaces with Conjure and then start the server again. I wish there were a solution that would stop, refresh and start each time I save a file.
I'm not sure if you suggest my current workflow in your first suggestion?
The browser refresh isn't important since that is done via shadow-cljs
I'll watch the video
If only working in the one namespace, then evaluating the whole namespace should update functions for routing and handlers in the REPL, but nothing outside those functions. (you dont actually need to save the file to evaluate, but its recommended to keep things in sync)
To reload everything, then clojure.namespace/refresh
is probably the simplest (although mount, integrant, component, donut could be added for this too, but for just a web server that is probably overkill)
There is a discussion about reloading with Conjure in this thread https://clojurians.slack.com/archives/CK143P6D7/p1669365292967419
Whey does below code giving 2 as output ? I expected value 4
(def the-world (ref "hello" :min-history 10))
(do
(dosync (ref-set the-world "better"))
@the-world)
(let [exclamator (fn [x] (str x "!"))]
(dosync
(alter the-world exclamator)
(alter the-world exclamator)
(alter the-world exclamator))
@the-world)
(ref-history-count the-world)
never really use refs and dosync during my 7 years of programming using clojure! because most of time, you need a database to coordinate the data, other than using refs in memory! most of the condition, atom is enough to define variable, and probably many condition, your’d using core.async channel and go thread to sequencial the event other than treat it as transactional, that’s useless most of the time
> All changes made to Refs during a transaction (via ref-set, alter or commute) will appear to occur at a single point in the 'Ref world' timeline (its 'write point'). > https://clojure.org/reference/refs from here
Haven't had need to use refs myself. So @U04V4KLKC's response did not seem all that helpful to me... Until I looked up the docs for dosync
, (which I also never needed to use), where I learned that dosync
is what makes this a transaction.
> (dosync & exprs)
> Runs the exprs (in an implicit do) in a transaction that encompasses exprs and any nested calls. ...
>
https://clojuredocs.org/clojure.core/dosync
So dosync
is creating a transaction context. All the expressions run in that context are applied as a single all-or-nothing transaction. Then as @U04V4KLKC pointed out, the whole transaction is recorded as a single write point.
Just trying out few things in clojure repl today , Why repl is not ending execution after running (execute-tasks)
(def task-queue (async/chan 4))
(defn send-task [ & tasks]
(doseq [task tasks]
(println "Adding task to the queue "task)
(async/>!! task-queue task )))
(defn execute-tasks []
(loop []
(when-some [task (async/<!! task-queue)]
(println (eval task))
(recur))))
(comment
(send-task `(+ 1 2))
(send-task `(+ 1 2))
(send-task `(+ 1 2))
(send-task `(+ 1 2))
(execute-tasks)
)
(when-some [task (async/<!! task-queue)]
will block forever trying to take from the task queue. You need to close the task when it is done, or put a value on the task queue to indicate to stop taking more
is there any way where I can check all task is in queue is removed > so that I can cam close the channel at that time
user=> (defn execute-tasks []
(future
(println "starting task running on background thread")
(loop []
(if-some [task (async/<!! task-queue)]
(do
(println (eval task))
(recur))
(println "stopping task running loop")))))
user=> (execute-tasks)
#object[clojure.core$future_call$reify__8544 0x59fa5a39 starting task running on background thread
{:status :pending, :val nil}]
user=> (send-task `(+ 1 2))
Adding task to the queue (clojure.core/+ 1 2)
nil
user=> 3
(send-task `(+ 1 2))
Adding task to the queue (clojure.core/+ 1 2)
user=> (async/close! task-queue)
nil
user=> stopping task running loop
Couldn't find a better channel for this, but I'm trying out Selmer and having issues with keywords.
I have them namespaced, like so: ::foo.bar/baz
and I'm trying to reach them in a template with {{foo..bar/baz}}
and it's not finding them. I can see from the docs and confirm in experiments that the template can find :foo.bar/baz
.
I could write a converter function, but that's just avoiding the issue.
::foo.bar/baz
this is a namespaced keyword, you should not be using these in templates. if you were to change the namespace name it will break your templates.
for the selmer part, i use keywords like :foo.bar/baz
without issue, almost all of the keywords i use in templates are namespaced, and many are namespaced with periods in the namespace part.
(render "{{person.name}}" {:person {:name "John Doe"}})
can you give an example like how the selmer docs do.
(def example-character
#:char{:name "Tuesday Slithen"
:level 4
:strength 2
:constitution 3
:dexterity 4})
{{unwarysage..grimm..domain..character/constitution}}
{{unwarysage..grimm..domain..character/dexterity}}
Is the template #:char{:name "Tuesday Slithen"
is weird, it may just be doing too much magic, can you use a regular clojure object instead?
So char
in clojure is aliased to unwarysage.grimm.domain.character
, but the template wouldn't know about that, so I typed it out fully in the template
i haven't tried this many periods in a namespace before in selmer, likely this is a problem
selmers code isn't as general as you may believe, so try the same thing but make the namespace with only a single period
if that works, then you can do something like clojure.set/rename-keys as a pre-processing step, and have cleaner templates as well
(render-character-sheet {} {:char/constitution 9})
this works, 9 shows up in the template
(render-character-sheet {} {::char/constituion 9})
, this doesn't.
As I understand it, ::
means that it's a hard alias. You have to have it imported to use it.
::
is a ns alias, it's useful for avoiding namespace collisions in keywords, you prob don't want to use it
I'm hoping to have a consistent set of specs for the system, hence the precisely namespaced keys.
Ah, I think I found my original issue. #::{}
produces a different map compared to #:{}
.
https://clojuredocs.org/clojure.set/rename-keys , https://clojuredocs.org/clojure.walk/prewalk-replace these 2 functions you can use to change your keywords for use in your template. it'll make your template much easier to read/write, and you have full control over the translations
i don't really want to tell you how to write your code, cus maybe you have a good reason for using ns style keywords. you have 2 options, one is to change your keywords in your code, and one is to change your keywords in your template. i think you should try both and settle on what you feel is better
no problem. if you have any other issues with selmer i can prob help. i use it a lot and have contributed to it a few times.
i'm not really sure what you are doing with selmer (if you are just playing around, or your app is serious), but i would recommend trying to write your code so that it doesn't depended too heavily on selmer for rendering templates. having your render function dynamically dispatch to selmer or some other template lib (rum/hiccup) can be very useful
Yeah. Was planning on making a generic "render" function accessible in the system map. I'd considered hiccup, wrapped in a cache if necessary.
i have found it useful to use a multimethod for rendering, i actually do recursive rendering in parts of my app where i can mix selmer and other templates inside each other.
(render {:template/type :selmer :template/file "file.html" :template/data {}})
something like that, though my data can contain other template defs, but i recommend you don't do something like that at first cus it's a little tricky.
Confusion about namespaced keywords and spec. I heard it's good to use namespaded keywords in my maps. Say I have a person model with fname and lname. Should the model be
{
::fname fname
::lname lname
}
or rather
{
:person/fname fname
:person/lname lname
}
And how do I write specs for that?
For example:
this implementation:
(def non-empty-string? #(not (str/blank? %)))
(s/def :person/fname
non-empty-string?)
(s/def :person/lname
non-empty-string?)
(s/def :person/person
(s/keys :req-un [:person/fname
:person/lname]))
(defn make-person [fname lname]
(s/assert :person/person
{:person/fname fname
:person/lname lname}))
passes this test
(deftest make-person-test
(testing "creating a person"
(are [fname lname person] (= (make-person fname lname) person)
"Fname" "Lname" {:person/fname "Fname" :person/lname "Lname"}
"Fname2" "Lname2" {:person/fname "Fname2" :person/lname "Lname2"}
)))
However, when I try to create a 'person' in the repl , I get an error:
*ns*
=> #object[clojure.lang.Namespace 0x768bdf96 "user"]
(people-i-know.model.person/make-person "Fname" "Lname")
Execution error - invalid arguments to people-i-know.model.person/make-person at (person.clj:17).
#:person{:fname "Fname", :lname "Lname"} - failed: (contains? % :fname)
#:person{:fname "Fname", :lname "Lname"} - failed: (contains? % :lname)
Obviously there is something about namespaces and specs I haven't understood.The un in req-un means unqualified (no namespace) so your person spec is for a map with the keys :fname and :blame, which is why the spec fails, and why it reports that your map doesn't contain those keys
I'm not sure about the test part, but your spec is failing because the keys are specified with :req-un
and make-person
is using qualified (ie. namespaced) keywords
you are both right! 🙂 I somehow had req-un(ion) in my mind and thought it meant to apply all specs for the keys. Thank you very much for your quick and correct answer. It is working now.
Also, I would probably recommend making an explicit spec for the non-empty string:
(s/def ::non-empty-string?
(s/and
string?
#(not (str/blank? %))))
(s/def :person/fname ::non-empty-string?)
this should make it possible to generate persons.
I think either could be fine depending on the use case and personal preference.
It's helpful if you (or someone who uses your specs) wants to do generative testing.
or generate examples
I think it might also slightly improve the output when explaining why a particular value doesn't conform.