This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-06-26
Channels
- # aleph (5)
- # announcements (16)
- # babashka (36)
- # beginners (161)
- # calva (24)
- # cider (8)
- # circleci (45)
- # clj-kondo (5)
- # cljs-dev (25)
- # cljsrn (5)
- # clojure (116)
- # clojure-europe (10)
- # clojure-nl (18)
- # clojure-uk (14)
- # clojuredesign-podcast (6)
- # clojurescript (50)
- # cursive (12)
- # data-science (8)
- # datomic (8)
- # duct (39)
- # emacs (6)
- # fulcro (21)
- # graalvm (12)
- # kaocha (17)
- # off-topic (184)
- # pathom (1)
- # pedestal (2)
- # re-frame (31)
- # reagent (24)
- # reitit (1)
- # sci (1)
- # shadow-cljs (23)
- # sql (147)
- # tools-deps (8)
- # vrac (3)
- # xtdb (35)
Whew I’m struggling! I’ve got a tiny clojure program running on a remote server, using deps.edn
and (hopefully) clj
. It’s a daemon/run-forever type script, and I’m having a lot of trouble figuring out how to “escape” the repl and leave the process running in the background
I’ve tried a number of things, but I started here clj -m <ns>
I have a -main function in that ns, and it’s called correctly (the code even works!)
/t/run ❯❯❯ cat deps.edn
{:paths ["."]}
/t/run ❯❯❯ cat foo.clj
(ns foo)
(defn -main [] (println "hello and goodbye!"))
/t/run ❯❯❯ clj -m foo
hello and goodbye!
/t/run ❯❯❯
indeed! I’ve got an infinite sequence thing going on
let me share a snippet
(defn start-schedule []
(-> (chime/periodic-seq (Instant/now) (Duration/ofMinutes (:interval-minutes env)))
(chime/chime-at (fn [time]
(println "Checking for dogs at " time)
(check-for-new-dogs)))))
(defn -main []
(start-schedule))
I’d like for -main to stay alive and continue to trigger per chime
’s schedule
but I’d also like to detach from this terminal’s process and kill my ssh connection
leaving clojure/java happily churning away on my remote machine
it does not, it consumes the shell and leaves me no choice but to CTRL-C it
yes. I’m open to opinionated suggestions! I’m not married to clj -m
I’ve tried bash-style backgrounding with & but that does something bad to the input/output streams
no to input. I do have some println statements; I’d like those be available if possible
it’s a remote server, so I suppose ideally they’d go to syslog or redirect to a file or somethin
they’re not particularly important overall
I think I’m having more luck now
I’m running the same command clj -m <ns>
but now it’s through systemd
that appears to be sufficiently “in the background”
there are various tools for this, in reverse order of hackiness: • run a repl inside tmux/screen and detach it from its controlling tty • use nohup to run from the background and log to a file • use jsvc (the unix version of Commons Daemon) a tool that lets a java program act as a proper service in a cross platform way • dedicated service integration tailored to your target OS (eg. systemd)
I have a shim java project that makes it easy to have jsvc use a specific clojure namespace, without needing AOT or interop in the Clojure code. https://github.com/noisesmith/clj-jsvc-adapter
it requires you to use a system property to register a clojure namespace containing regular functions to be used for each jsvc lifecycle method
oh and another option is to use Docker plus a microservice distsys app like mesos / k8s
(what my company actually does)
cool, thanks for enumerating all those options
I was ready to go docker plus some orchestrator
but it's just a lil DO droplet, didn't wanna burn any more resources than I already was
I really wanted a socket repl server to work
but that seems like it doesn't wanna be background-ed either (unless you do something fancy like your tmux suggestion)
nohup
sounds exactly like what I wanted
thanks all for chiming in!
with my lib, jsvc isn't hard to use, and the advantage of jsvc is you get restarting / monitoring / proper system level logging
but if nohup is enough and you don't mind manually checking and restarting, cheers 🍻
whale I'm not gonna UNDO my systemd work
socket-repl and nohup are compatible
unless you think there's a limitation of systemd I might run into?
oh no, beyond portability concerns (which don't seem like an issue, and you'll know what they are) systemd is fine
ah portability
thanks much @U051SS2EU this was educational 🧠
> * run a repl inside tmux/screen and detach it from its controlling tty
> * use nohup to run from the background and log to a file
Hasn't there been issues of systemd killing such procs when they lose their tty, and weren't run with systemd-run
?
last I used those techniques was pre-systemd honestly
I mean, I'm a professional after all and those are hacky solutions :D
worse is better, but I strive for better than that, to coin a phrase
If I have the following map {:players {:player1 {:hand :rock}, :player2 {:hand :rock}}}
as input to the following:
(defn determine-winner
[game]
(let [player1-hand (get-in game [:players :player1 :hand])
player2-hand (get-in game [:players :player2 :hand])]
(if (= player1-hand player2-hand)
:draw
(if (= (player1-hand dominated-by) player2-hand)
:player1
:player2))))
How can I better destructure the input? In other words, do you reckon the let
block is fine in this case or is there a more idiomatic way to do this?
@rameezkhan.sa I'd probably declare the argument to be [{:keys [players]}]
and then your let
bindings are one level less.
(since you're not using game
anywhere else in the function)
@seancorfield Cool! Any way to destructure "deeper" than that even?
There is, but I'm not sure I'd find it easier to read than the code you already have
Nested destructuring can be very hard to read, in my opinion.
Fair enough. Thanks @seancorfield 🙂
Next question. 🙂 I'm learning spec
and have the following:
(ns rock-paper-scissors.core
(:require [clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as g]))
(def hand? #{:rock :paper :scissors})
(s/def ::hand hand?)
(s/def ::player (s/keys :req [::hand]))
(s/def ::player1 ::player)
(s/def ::player2 ::player)
(s/def ::players (s/keys :req [::player1 ::player2]))
(s/def ::game (s/keys :req [::players]))
(def dominates
{:rock :scissors
:paper :rock
:scissors :paper})
(g/sample (s/gen ::game) 5)
g/sample
returns
Class: clojure.lang.LazySeq
Contents:
0. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :scissors }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :scissors } } }
1. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :scissors }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :rock } } }
2. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :rock }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :rock } } }
3. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :rock }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :scissors } } }
4. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :scissors }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :scissors } } }
g/sample
produces a sequence of maps. I'm not sure what you're asking.
Sorry, to be more clear, what I'm asking if those sequence of maps can return {:players {:player1 {:hand :scissors}, :player2 {:hand :scissors}}}`` for item 0 above. Does that make sense?
No, I don't understand your question.
Your specs use qualified keywords. What did you expect to get from g/sample
?
The output above is correct.
(I don't know what you're using to run the code -- a regular REPL produces much nicer output than that)
Ah wait, I think I understand. I didn't realize :rock-paper-scissors.core/players
was an actual keyword. I was expecting just :players
. But I get it now.
Thanks for the help @seancorfield!
In a normal REPL those maps would be a lot easier to read
Hmm, maybe not because they are nested. Anyways, they are exactly what I'd expect to get -- qualified keywords.
When running a program with lein run :hi "there"
is there a way to tell the main function to get the args as a dict? Or do I have to write that code myself?
'(":hi" "there")
My current code for turning the args into a dict is
(into {} (for [[k v] (partition 2 args)] [(symbol k) v])
dunno if there is a better way to do it :)I think you have to parse it - something like:
lein run '{:a 1 :b 2}'
;; the in code
(clojure.edn/read-string arg)
I'm not saying it's better, but here's another way:
(->> '(":hi" "there" ":foo" "bar")
(partition 2)
(map (juxt (comp symbol first) second))
(into {}))
;;=> {:hi "there", :foo "bar"}
Thanks both of you 🙂
Does tools.cli work easily with lein run
?
I have code like this:
(def rules (atom {}))
(defmacro defrule
"TODO: test me."
[name & body]
`(do
(let ["bla" "bla]
(swap! rules assoc kw-name# (handle-docs body#)))))
What the code does is allow users to define rules. With the macro, they are all collected into the atom rules
.
Is there a way to do this in a stateless way - i.e. without the atom?If they are dynamically(during runtime) changing the rules i dont think there is stateless way. However if the rules are only initialized at startup or obtained somewhere but without dependency on a particular point in time(like every 5 minutes?) - I recommend memoize
Another way that you can store that state, is as metadata in def
/`defn` statements that you output from that macro. You can then iterate over the symbols in the ns and filter for the metadata.
I just happened to find it: Metabase's defendpoint
generates compojure routes, but with :is-endpoint? true
in the metadata https://github.com/metabase/metabase/blob/75331072c0bbfb31b2edd5a2390324b5a537439c/src/metabase/api/common.clj#L263
It is later used to generate API docs from the docstrings https://github.com/metabase/metabase/blob/75331072c0bbfb31b2edd5a2390324b5a537439c/src/metabase/cmd/endpoint_dox.clj#L19
Thanks mm and walterl. @UJY23QLS1 I think clara-rules also stores its rules with meta-data. I do not see the advantage over just having an atom though. See https://github.com/cerner/clara-rules/blob/8e5714216fa2102cd8b09e3cc73f01048154878f/src/main/clojure/clara/rules.cljc#L384
The difference, I think, is that metadata is "more static" than using an atom, and I would suggest using what fits best. If the macro string metadata (conceptual, rather than Clojure-specific), store it in (Clojure) metadata, if it's collecting data you're working with, an atom is probably more suitable.
I think what bothers me about hard-coding an atom in a macro like that, is that it makes "magic" out of the data collection being performed by the macro, by not referencing the atom in its usage. I.e. when reading a (defrule ...)
usage, there is no hint that the rules
atom is being used.
Good points. I will collect metadata instead
Hi, I am currently refactoring some of clojure codebase, one of the easy targets for me is to provide a way to access same config in different flavours in lein. I saw that you can use :profile
in project.clj
.
I want to have 2 configs:
:test
and :dev
how would I go about that in code, so when the lein test
is called it loads a different file from profile.
Have a look at this: https://github.com/metabase/metabase/blob/master/project.clj#L19
Aliases like those are used directly with lein. So you just run lein test
, and it's "expanded" (internally) to lein with-profile +test test
that's fine - but how do i later access that variable?
:profiles {:dev {:env {:config-path "conf/app.conf"}
:test {:env {:config-path "conf/app.test.conf"}}
I presume it will be available in env?@US9EF3BGU, don't forget to add the lein-environ
plugin. See more in https://cljdoc.org/d/lein-environ/lein-environ/1.2.0/doc/readme
with-profile +test
runs your tests with test
profile. with-profile +abc
runs your tests with abc
profile
Using +abc
adds the abc
profile to the default ones, right? If you run it without the plus sign, than it effectively replaces the profiles.
Maybe my interpretation is wrong, but I saw that "The above invocations [without plus sign] activate the given profiles in place of the defaults. To activate a profile in addition to the defaults, prepend it with a +
" (https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md#activating-profiles). What am I missing?
Ah you are completely right. Just tested it. The default profiles load in addition to the profiles specified in with-profile
. Good to know 🙂
Thank you for testing. I was kind in doubt after you said it overrides hehe Cool it is now clarified.
hello everyone, does any library exist that would make it easier to consume bytes from a websocket until a particular delimiter is reached, rather than just looking at individual frames?
i suppose it would be reasonably straightforward to build such a thing with core.async but i’d rather not duplicate the effort if possible
I have installed clojure into Ubuntu. I have : openjdk 14.0.1 2020-04-14 OpenJDK Runtime Environment (build 14.0.1+7-Ubuntu-1ubuntu1) OpenJDK 64-Bit Server VM (build 14.0.1+7-Ubuntu-1ubuntu1, mixed mode, sharing) I have a simple.core.clj file in /simple/src/simple and when I run clojure -m simple.core, I receive the following: clojure -m simple.core Execution error (FileNotFoundException) at clojure.main/main (main.java:40). Could not locate simple/core__init.class, simple/core.clj or simple/core.cljc on classpath. Full report at: /tmp/clojure-15616630179164666709.edn I installed apt install clojure however I suspect I did not generate the .clojure folders and all the edn files. Well, I cant see them - e.g. the should be a deps.edn in the /simple folder
which "clojure" tool are you using?
usually they would expect simple.core
to be in ./src/simple/core.clj
or alternatively ./src/clj/simple/core.clj
I would suspect that sudo apt install clojure
is something that very few production Clojure users ever touch or recommend.
yeah, debian's clojure doesn't use deps.edn, or even project.clj - it's a hacky standalone
the proper way to use clojure (as with any java lib) is with a project manager that manages your deps and classpath
it's not impossible to manage your own classpath, but there's little benefit
I would recommend sudo apt purge clojure
, followed by using the instructions here: https://clojure.org/guides/getting_started. If you want to use Leiningen, that is another common way, but only necessary if you want to interact with a project that someone developed using Leiningen (indicated by presence of a project.clj file in the code base)
OK I will follow the guide
Funny enough the linuxbrew did not work either. I am trying to learn how to move away from leiningen
I know it might seem strange, but I believe that the packaged versions in Debian/Ubuntu, and a few other package managers, are created by enthusiasts of those packaging systems, and also of Clojure, but what they install often has little to do with the http://clojure.org instructions.
or worse, puts commands in your command path that have the same names as the installers on http://clojure.org, which thus shadow/conflict with the recommended ones.
When you say the linuxbrew did not work either, do you mean the instructions on http://clojure.org did not work for you on Linux?
Followed https://clojure.org/guides/getting_started. and doing the obvious works 😠
ring doesn't actually say anything about routes, routing will come from some other library
all you can do is invoke the function on a request and either get a response when it matches, or nil when it doesn't
https://github.com/metosin/compojure-api/blob/master/src/compojure/api/routes.clj#L45-L52 will work, as long as you only use the stuff from comojure-api, if you use vanilla compojure or wrap things in middelware it won't
the top of the compojure-api readme says "Psst! If you're starting a new project, why not try out https://github.com/metosin/reitit?"
Glad you mention reitit! My first project I used reitit but could not generate a war file! Still unable to. so for my second i did compojure and that worked out well. I like the work the metosin guys do so when i saw they created compojure-api i wanted to use that. Able to create a war file but my latest problem is
@U06GMV0B0 I'm curious: why are you trying to generate a WAR file?
I figured the war file would be the least intrusive for our devops team. We are predominantly dotnet shop and use azure service fabric. I'm not officially a developer just love to dabble so I like to make sure what i deliver is hassle free but my primary reason for it is i assume that tomcat manages memory and keeps apps alive especially after reboots. If embedding jetty gets me there without complication then I can definitely pitch that.
You could use embedded Jetty and package the app as an uberjar, so it could be run with java -jar my-clojure-app.jar
if devops would go for that.
to resolve this issue i comment out just these 2 and everything works (there are 65 others)
(GET "/tasks/" req
`:return [Task]`
`:query-params [subprojectid :- Int]`
`:summary "Returns a list of tasks for a given subproject"`
`(get-tasks db (:params req)))`
`(GET "/tasks/:taskid" req`
`:return Task`
`:path-params [taskid :- Int]`
`:summary "Returns task details"`
`(get-task db (:params req)))`
You can use triple backticks around a multi-line block of code. It's more readable than having each line formatted with single backticks.
like this
block
of text
(GET "/tasks/" req
:return [Task]
:query-params [subprojectid :- Int]
:summary "Returns a list of tasks for a given subproject"
(get-tasks db (:params req)))
(GET "/tasks/:taskid" req
:return Task
:path-params [taskid :- Int]
:summary "Returns task details"
(get-task db (:params req)))
this is why i was wondering if there was a way to pull the "routing table" and see if i have any duplicates/conflicts
Why this is returning nil
instead of ""
(re-matches #"[a-z]{2}?" "")
This works as expected:
(re-matches #"[a-z]{2}?" "fr")
that regex doesn't match that string
this one does
(ins)user=> (re-matches #"(?:[a-z]{2})?" "")
""
(ins)user=> (re-matches #"(?:[a-z]{2})?" "fr")
"fr"
(?: ... )
is a "non capturing group", relatively obscure but exactly the right thing here
note directly, as you can't properly use a macro as an argument to a function
Does anybody else have trouble getting Firefox to read edn properly in the response devtools?