Fork me on GitHub

What are ways to securely be able to "repl into" my app in production?


Setup a Socket repl for your app listening on localhost, ssh into the prod machine, then nc localhost &lt;port&gt;

👀 1

Is it normal for a clojure program to take 30s to 1 minute to start?


Startup time has a strong dependency on the program itself. It might be normal if it require some preparations like connecting to external systems and collecting some information.


I'm just trying to launch a repl. Even using integrant and assuming it's connecting to a local postgres and kafka before it lets me type in the repl, this seems like a strange amount of time

Ben Sless15:10:20

Are you using leinigen? Do you have a large class path? What JVM opts did you start with?


Using leinigen through intelliJ, JVM opts are

Have 50 dependencies in profile.clj, not sure if that is considered a lot


is there a way to enumerate all namespaces for my project? in other words, can I make (ns-all) work only for :paths, not for :deps?

Bob B16:10:12

I imagine you could probably filter the output of (all-ns) with str/starts-with? (once you get the names) or something like that, but I think that by the point you're calling (all-ns), Clojure is basically only aware of 'the classpath' as a thing, with no distinction between what was contributed to 'the classpath' via :paths vs :deps - I could totally be wrong, though


I think filter by starts-with is the only way for now. I’ll try that. Thanks!

Vettukal Vincent Mathew17:10:48

Hi everyone, I am new to clojure and was trying out next-jdbc

(jdbc/execute-one! ds ["
insert into address(name,email)
  values('Someone Else','')
#:next.jdbc{:update-count 1}
I was wondering how to get just the map of {:update-count 1} out of this. Also {:builder-fn rs/as-unqualified-lower-maps} works for select query but not insert.


(get #:next.jdbc{:update-count 1} :next.jdbc/update-count) #_ -> 1


(= #:next.jdbc{:update-count 1} {:next.jdbc/update-count 1}) is true. That prefix on the map is just a shorthand for indicating all of the keys in the map have that namespace. And you can turn it off if you like:

(set! *print-namespace-maps* false)
user=> #:next.jdbc{:update-count 1}
{:next.jdbc/update-count 1}

🙏 1
Vettukal Vincent Mathew17:10:34

Thanks @dpsutton that did work. (= #:next.jdbc{:update-count 1} {:next.jdbc/update-count 1}) That does explain whats happening here. Never saw namespace-maps while studying any of the clojure books and got confused.

👍 2

If you want, you could an also destructure it, i.e., (let [{:next.jdbc/keys [update-count]} (jdbc/execute-one! ds .......)] (println "number of rows updated " update-count))

🙌 1

More info here

Zach Mitchell, PhD18:10:27

I have a Clojure API design question. I have a function foo that takes an integer n and returns a map. It is incorrect to call this function with n < N. So, my question is what is the idiomatic way to handle when someone calls foo with n < N? Raise an exception? Return nil? Try to catch n < N beforehand so that foo doesn't have to worry about it?

R.A. Porter18:10:31

There are several ways of solving that. You could use a :pre condition (, or an fdef ( or hand-code an assertion check of your own if you want to have more control over the behavior of the fn when called with semantically incorrect input.


I'm not sure what is considered idiomatic, but my experience is that Clojure generally considers the response to invalid input to be "undefined" (meaning, "all bets are off"), unless specifically stated. In your specific example and without knowing more about the context, I'd probably just return nil and mention that behavior in the docstring.


This is always a “your system” type of question. What do you think should happen? Is this bad but innocent data? Failure to validate human input? A broken invariant that absolutely should not be able to happen? Recoverable? Fatal? Amusing?

☝️ 2

Hello everyone! Are there any good tools that make clojure's stack traces more readable? Do you personally use such tools or is there a reason not to? Other languages seem to do a much better job at telling you what went wrong. I've seen some people say it's something you learn to work with, which I find hard to believe because usually the stack trace is a lot more cryptic than "can't cast one class to another". Sometimes I think that a better error message could have saved me a lot of time. Thanks in advance.


This blog post gives some decent advice, What IDE are you using? There might be some more specific tools that are available depending on your setup.

👍 1

@U7RJTCH6J Thank you. I'm using neovim as my IDE.


I'm a weirdo that has never been bothered by the stack traces. I think some mitigations clojure has to make stack traces less troublesome: • easier to build programs in small pieces and then compose the pieces to make larger programs • repl driven development for tighter feedback loops If you haven't tried repl-driven development, I highly recommend it. It accentuates clojure's strengths and minimizes its weaknesses.


Unfortunately, I'm unfamiliar with neovim. I'm not sure what options might be available to help with stack traces.


Yes, I'm using vim-iced for interacting with REPL and it is awesome. Thanks for the advice!


My usual work flow is similar to what's recommended in the blog post. Find the stack trace that points to a line in my current namespace and work from there.


I came across recently. Haven’t tried it, but it looks nice

👍 1

Do you have a stack trace that you can put in here and we can look at it together?


@dpsutton It's not really a problem with any particular stack trace, but rather me not being used to reading clojure stack traces. There was a particularly annoying one today, though. I was going to post it here, but while copying noticed that the reason was stated in plain english 🙂 It was somewhat buried, but I still feel silly. I am reading the article that @U7RJTCH6J suggested and it seems that it really is a question of getting used to it. Thanks for being so ready to help though. I'm glad I've discovered clojurians.


I think that’s the crux of it. It’s easy for your eyes to gloss over at first, but if you take a breath the stack trace will point exactly to the offending code and often has a very useful message


Yep, I guess I'm just more used to reading shorter error messages in other languages. But I'm going to practice then.

👍 1

If you do find yourself confused after a minute of reading drop it here in a snippet (so it’s collapsing) and ask for help


I’m sure there’s still room for improvement as it’s certainly not just you. I think there has been quite a bit of improvement in clojure core, but I think the tools that surface the stack traces are still integrating the newer/better info that’s now available.

👍 1

Okay, will do


Yes, I've read that the situation has improved since the release. It must not be so easy to implement into the core language.


Hey, when building an uber jar with the build command clj -T:build uber also starts the program. Is there a flag or something for just building?


I don't know if this is needed as context

(defn uber [_]
  (clean nil)
  (b/write-pom {:class-dir class-dir
                :lib lib
                :version version
                :basis basis
                :src-dirs ["src"]})
  (b/copy-dir {:src-dirs ["src" "resources"]
               :target-dir class-dir})
  (b/compile-clj {:basis basis
                  :src-dirs ["src"]
                  :class-dir class-dir})
  (b/uber {:class-dir class-dir
           :uber-file uber-file
           :basis basis
           :manifest {"Main-Class" "lhrb.wdylt.server"}}))


Do you require any namespaces from your program in build.clj?


Are there any top level forms that have side effects other than defining constants or functions?


not that Im aware of

(ns build
  (:require [ :as b]))

(def lib 'lhrb/wdylt)
(def version (format "1.0.0"))
(def class-dir "target/classes")
(def basis (b/create-basis {:project "deps.edn"}))
(def uber-file (format "target/wdylt.jar"))

(defn clean [_]
  (b/delete {:path "target"}))

;; build with clj -T:build uber
(defn uber [_]
  (clean nil)
  (b/write-pom {:class-dir class-dir
                :lib lib
                :version version
                :basis basis
                :src-dirs ["src"]})
  (b/copy-dir {:src-dirs ["src" "resources"]
               :target-dir class-dir})
  (b/compile-clj {:basis basis
                  :src-dirs ["src"]
                  :class-dir class-dir})
  (b/uber {:class-dir class-dir
           :uber-file uber-file
           :basis basis
           :manifest {"Main-Class" "lhrb.wdylt.server"}}))




:manifest {"Main-Class" "lhrb.wdylt.server"} I copied this from one of the test cases everything else is from the offical guide


if you comment out all of the forms in your uber function so it does absolutely nothing, does it still run your program when running clj -T:build uber?


let me check


no it does not


(b/compile-clj {:basis basis
                  :src-dirs ["src"]
                  :class-dir class-dir})
this seems to run the program


I use (gen-class) in this namespace


that's likely not the problem. Compiling is reading the clojure source files and writing the bytecode to classfiles. You have some top level def that does work rather than can be invoked to do work


such as this one

(def server (jetty/run-jetty #'app {:port 3000
                                    :join? false}))


what about in the lhrb.wdylt.server namespace or its requires?


this is my main ns


with the main method inside


if you eval that form you don't have a function that can create a webserver you are running a webserver


tbh I don't know yet how to eval forms in the build.clj file 😇


I was just reactivating one form after another and run clj -T:build uber


clj -A:build and then just load the file


or eval the namespace form and each of the other forms you need (which is exactly what load-file does)


usually the important bits are just to have on the classpath and then you can just eval that file as you like. Note it is not on the classpath normally unless your classpath has . on it (the root directory in addition to just src/ or whatever


guys sry my knowledge around tooling is limited


I use cider


and when I jack-in and try to eval something in the build file i get a filenotfoundException


1. Unhandled
   Could not locate clojure/tools/build/api__init.class,
   clojure/tools/build/api.clj or clojure/tools/build/api.cljc on classpath.


For these purposes, build is just another alias that includes an extra dependency and specifies to use the build namespace


that means you don't have on your classpath


so you cannot use the dependency


:aliases {:build {:deps {io.github.clojure/ {:git/tag "v0.6.2" :git/sha "226fb52"}}
                   :ns-default build}}}
I have this in my deps file


and did you start your repl with the build alias included?


:paths ["src" "resources"]
these are the configured paths


that's not automatic


okay I did not


do you have any resource where I can look up how to include a namespace with cider, when starting a repl?


if you do C-u M-x cider-jack-in, then it let's you edit the command


Where would I put the build alias?

:aliases {:cider/nrepl {:main-opts ["-m" "nrepl.cmdline" "--middleware" "[refactor-nrepl.middleware/wrap-refactor,cider.nrepl/cider-middleware]"]}}


there should be a whole command there. with the beginning /usr/bin/clojure blah blah


or you can edit the cider-clojure-cli-parameters emacs variable, but I forget how to do that as a one off


yes it is like this

/usr/bin/clojure -Sdeps '{:deps {nrepl/nrepl {:mvn/version "0.9.0-beta2"} refactor-nrepl/refactor-nrepl {:mvn/version "3.0.0-alpha13"} cider/cider-nrepl {:mvn/version "0.26.0"}} :aliases {:cider/nrepl {:main-opts ["-m" "nrepl.cmdline" "--middleware" "[refactor-nrepl.middleware/wrap-refactor,cider.nrepl/cider-middleware]"]}}}' -M:cider/nrepl


change it to -M:build:cider/nrepl


ty guys this worked


probably wouldn't* be that hard to add a command to your emacs config that starts a build repl


I should probably do that for my setup


Guys thank you so much. I figured it out. It was actually the (def server ...) form which led to the behavior


Damn I was wrong it still starts the program in background :thinking_face: this is odd isn't it


did you remove the use of (def server ...)?


what did you replace it with?


I put it into the-main fn as let


(defn -main []
  (let [server (jetty/run-jetty #'app {:port 3000
                                       :join? false})]
   (log/error "damn this gone horrible wrong")
   (schema/create-db-structure ds)
   (start server)
   (.. (Runtime/getRuntime)
       (addShutdownHook (proxy [Thread] []
                          (run []
                            (log/error "shutdown hook")
                            (.stop server)
                            (System/exit 0)))))))


the build does not start the server


but it starts the program for some reason


clj -T:build uber
21:59:00.378 [main] DEBUG org.eclipse.jetty.util.log - Logging to Logger[org.eclipse.jetty.util.log] via org.eclipse.jetty.util.log.Slf4jLog
21:59:00.384 [main] INFO org.eclipse.jetty.util.log - Logging initialized @1815ms to org.eclipse.jetty.util.log.Slf4jLog
WARNING: set already refers to: #'clojure.core/set in namespace: lhrb.wdylt.server, being replaced by: #'honey.sql.helpers/set
WARNING: into already refers to: #'clojure.core/into in namespace: lhrb.wdylt.server, being replaced by: #'honey.sql.helpers/into
WARNING: group-by already refers to: #'clojure.core/group-by in namespace: lhrb.wdylt.server, being replaced by: #'honey.sql.helpers/group-by
WARNING: update already refers to: #'clojure.core/update in namespace: lhrb.wdylt.server, being replaced by: #'honey.sql.helpers/update
WARNING: partition-by already refers to: #'clojure.core/partition-by in namespace: lhrb.wdylt.server, being replaced by: #'honey.sql.helpers/partition-by
WARNING: for already refers to: #'clojure.core/for in namespace: lhrb.wdylt.server, being replaced by: #'honey.sql.helpers/for
WARNING: filter already refers to: #'clojure.core/filter in namespace: lhrb.wdylt.server, being replaced by: #'honey.sql.helpers/filter


(ns lhrb.wdylt.server
    (:require [ring.adapter.jetty :as jetty]
              [ring.middleware.params :as params]
              [reitit.ring.middleware.muuntaja :as muuntaja]
              [muuntaja.core :as m]
              [reitit.ring.coercion :as coercion]
              [reitit.ring :as ring]
              [hiccup.core :as html]
              [clojure.spec.alpha :as s]
              [next.jdbc :as jdbc]
              [honey.sql :as sql]
              [honey.sql.helpers :refer :all]
              [lhrb.wdylt.migration :as schema]
              [ :as log])


do you see anything else? those are all warnings from compiling code. and initializing a logger isn't crazy


building is going to load the main namespace and its requires which will evaluate all of the top level forms


ah right okay nvm sry guys


I guess it was just a zombie process which I saw than


don't know how to read the monkey ha. is everything working as you expect or still having issues?


It is now working as expected


Thanks a lot for helping me out

👍 1