Fork me on GitHub
#beginners
<
2021-08-17
>
Rowan Barnard08:08:45

Hey guys, I'm currently working through the D. Sotnikov book 'Web Dev with Clojure, 3rd Ed.' where I package a Luminus webapp as an uberjar and then run it with the JVM. But then I am not sure how to shut down the web server once it's running? I just used ps -A |grep java and then kill -9 followed by the PID to shut it down but I'm not sure if this is the proper way to shut down a web server. Would this way cause issues with unclean shutdowns particularly with regards to the database connection? I'm not experienced with web dev, just so you know 😅

mjw13:08:13

If it’s running in a terminal, you should be able to stop it with Ctrl-C, which will be how you can stop just about any running process in a terminal.

emccue13:08:37

Well, generally you run uberjars in production. In that environment the proper way to kill a server is to send a shutdown message and then kill the machine

emccue13:08:53

its not guaranteed to run, but you can add shutdown hooks to the jvm that it will run on a best effort basis for cleaning up db connections

Rowan Barnard23:08:41

Thank you for the answers 🙂

Malik Kennedy01:08:38

ring.adapter.jetty/run-jetty returns a object with .stop method.

Malik Kennedy01:08:27

(defonce server (jetty/run-jetty #'router {:port 8000 :join? false})) (.stop server)

Rowan Barnard01:08:57

How would I call it do you know?

Rowan Barnard01:08:00

I think maybe Ctrl + C is all I need to stop the server as I managed to find info on Jetty which said to use Ctrl + C to stop it. Thanks for the info on the .stop method, I'll look into that too 🙂

finchharold08:08:52

Hi all, how to just take the value here [{:items 3}] ?

delaguardo08:08:31

which one? there are many ) [{:items 3}], {:items 3}, :items and 3

delaguardo08:08:57

to access deeply nested value there is cool function get-in

delaguardo08:08:36

keep returns a lazy sequence of the non-nil results of applying function (in your example :items to each item of collection.

Rowan Barnard08:08:55

(:items (first [{:items 3}]))

Rowan Barnard08:08:11

The map {:items 3} is the first element in the vector and then the keyword :items can be used as a function call to produce the value associated with it from within the map.

Rowan Barnard08:08:11

Most Clojure beginner's books have a chapter on destructuring or you can look that up online for info on how you can grab stuff from within nested data structures.

emccue13:08:14

(let [value [{:items 3}]]
  (= (get-in [0 :items] value)
     (:items (first value))
     (first (map :items value))
     (:items (nth items 0))))

sova-soars-the-sora16:08:07

(val (first [{:items 3}])) would also work methinks

delaguardo17:08:17

yes, together with other N different solutions )

quoll20:08:50

That last one misses a level. val works on a key/value pair from a map. You usually get these when iterating through a map (i.e. when treating the map as a seq). That can work on the first (and only) entry in the map. However, first here will return the entire map. You then need to get the first in there. So you can use: (val (first (first [{:items 3}]))) But getting the first-of-the-first item is a common enough idiom that there is a shortcut function for you: ffirst (val (ffirst [{:items 3}]))

quoll20:08:14

Note that I wouldn’t use any of the suggestions here 🙂

quoll20:08:50

If extracting deeply nested values, I tend to either use threading macros (since these make the path down to the item clearer to read) or destructuring. So given: (def the-value [{:items 3}]) Then with a threading macro: (-> the-value first :items) Or, if it’s not necessarily in the first position, nth can be used: (-> the-value (nth 0) :items) But I usually want to extract the value into a var, in which case I can destructure for it in a function definition, or a let block: (let [[{v :items}] the-value] ...) or (defn my-fn [[{:keys [items]}]] ...) (note that I renamed the items value in the let destructuring, and kept the name via :keys in the second. There is no significance to this)

sova-soars-the-sora18:08:21

Neat. Thanks for that elucidation ^.^

Franco Gasperino14:08:08

Once a value has defined in a namespace by the reader, can that value be undefined, specifically when working within the repl? I understand using comments for testing, however a refactor can lead to changing around values (def/defn) in the namespace.

dpsutton14:08:33

are you asking if you can undefine it intentionally or if it can become undefined unintentionally?

dpsutton14:08:44

user=> (doc ns-unmap)
-------------------------
clojure.core/ns-unmap
([ns sym])
  Removes the mappings for the symbol from the namespace.

dpsutton14:08:19

user=> (def foo 3)
#'user/foo
user=> foo
3
user=> (ns-unmap *ns* 'foo)
nil
user=> foo
Syntax error compiling at (REPL:3:1).
Unable to resolve symbol: foo in this context

Alex Miller (Clojure team)14:08:49

namespaces are essentially mutable (ouch, it hurts) maps of names to vars

Franco Gasperino14:08:24

The calva extension to VScode continues to see the variables as read by the reader, previously, even though the variables have been manually been changed, intentionally

Franco Gasperino14:08:06

variable hints and similar features require me to stop the repl after time

dpsutton14:08:16

the calva tooling is running inside of your program. it doesn't look at the text file really but introspects the namespaces themselves. so if it is still defined in the ns it will offer it as a suggestion

Franco Gasperino14:08:02

understandable as to how/why and it's appreciated. but can become somewhat cumbersome after a week or so of significant changes

solf15:08:33

If you need to clean-up a namespace, you can try something like this:

(map (partial ns-unmap *ns*) (ns-map *ns*))

Franco Gasperino16:08:04

what does that do to external namespaces with an existing dependency?

Franco Gasperino17:08:07

Any core approach to perform the following?

(defn assoc-in* [m ks]
  (reduce
   (fn [l r] 
     (let [x (- (count r) 1)]
       (assoc-in l (subvec r 0 x) (nth r x)))) m ks))

=> (def orig {:a {:b {:c 1 :d 2}}})
=> (assoc-in* orig [[:a :b :d 3]])
  {:a {:b {:c 1, :d 3}}}
=> (assoc-in* orig [[:a :b :e 4]])
  {:a {:b {:c 1, :d 2, :e 4}}}
=> (assoc-in* orig [[:aa 1] [:a :b :c {:cc 1}]])
{:a {:b {:c {:cc 1}, :d 2}}, :aa 1}

Russell Mull17:08:14

your first two examples are very close to regular assoc-in already:

(assoc-in origin [:a :b :d] 3)

noisesmith17:08:04

also, you can replace (subvec r 0 x) with (pop r) and (nth r x) with (peek r)

Franco Gasperino17:08:44

The last example is why i'm curious. I have a use case where i want to update 3+ nested map values, and this occurs in several different locations

noisesmith17:08:06

and not relevant to the question presented, but I'd move m and ks to a new line (or a new line each) as they belong to a lower indentation than the start of the line they are on

Russell Mull17:08:31

reduce seems a fine way to do it, in that case.

Russell Mull17:08:23

And yeah, @U051SS2EU’s advice about the whitespace is good. I thought you made a mistake with reduce, initially.

Franco Gasperino17:08:47

thanks for the tip. i hadn't read that pop and peek were collection dependent behavior

Franco Gasperino17:08:07

(defn assoc-in* [m ks]
  (reduce #(assoc-in %1 (pop %2) (peek %2)) m ks))
Much more terse

noisesmith17:08:40

another random code-review type thing, in clojure.core, foo* is always a lower level thing that's used to implement foo, so you might want to use some other differentiating suffix

pez22:08:29

Can I use the clojure command to only download the project dependencies? I.e. not run the project or drop me into a REPL prompt.

dpsutton22:08:28

clojure -P should do that

dpsutton22:08:55

from clojure --help : > -P Prepare deps - download libs, cache classpath, but don't exec

pez22:08:08

Thanks, I actually didn’t see it in the help. We should create a https://github.com/tldr-pages/tldr page for it. 😃 (Not joking, actually, just looks friendlier with a smiley 😂 )

dpsutton22:08:13

big fan of tldr

pez22:08:14

OK. So now I understand why I didn’t see it. On the machine I checked it first it wasn’t there. It has version 1.10.1.536. Thing is, clojure is freshly installed. Even if I upgrade I get this:

gitpod /workspace/pirate-lang $ brew upgrade clojure/tools/clojure
Warning: clojure/tools/clojure 1.10.1.536 already installed
I guess I should bring this up with the Gitpod peeps…