Fork me on GitHub
#clojure
<
2019-06-25
>
theeternalpulse04:06:43

I want to run an nrepl alias and spawn a jetty server in another thread so I can hold a reference to it to .start and .stop it but when I try to do so using a future (def server (future (run-jetty #'my-app {:port 3000}))) I don't think it is working. The main reason I want to do it this way is to spawn the jetty server and run the nrepl in one alias rather than start an nrepl and then another alias for the server.

theeternalpulse04:06:23

:nrepl {:extra-deps {nrepl {:mvn/version "0.6.0"} 
                       cider/cider-nrepl {:mvn/version "0.21.1"}}
          :main-opts ["-m" "nrepl.cmdline"
                      "--middleware" "[cider.nrepl/cider-middleware]"
                      "--port" "9000"]}
  :dev-server {:main-opts ["-e" "((requiring-resolve,'))"]}
this are my two aliases, but kind of want to combine them

didibus04:06:11

Hum... not sure you can combine main-opts

didibus04:06:26

Or, if you can, this won't really make sense

didibus04:06:48

Since I don't think nrepl.cmdline has a -e option

didibus04:06:03

Your best option might be to have a user.clj

didibus04:06:09

Where you start jetty

didibus04:06:36

Or just start nrepl inside of -e as well

theeternalpulse05:06:30

I think I'll add htat to my dev-server code, one question though is still launching the dev server and having a reference to it in the repl.

didibus05:06:23

Well, the REPL will have access to whatever. So if you keep a reference of the dev server anywhere, like just a global def, you should be able to access it

theeternalpulse05:06:29

ah, I htink this puts me on the right track. Is the user.clj where generally run-scripts for tools.deps ususally kept?

didibus05:06:58

user.clj is the namespace that is loaded by clojure.main by default.

didibus05:06:09

So if you put code in there, it will be loaded and evaled

didibus05:06:14

When you start clojure.main

didibus05:06:35

And nRepl also uses user as the entry namespace, so it will do the same

didibus05:06:04

So if you just connect to an nRepl, it would load the user.clj file

didibus05:06:11

And run whatever code is there as well.

didibus05:06:35

So it is often used for pre-running code on startup only for development

didibus05:06:31

Is run-jetty a blocking command?

didibus05:06:42

Is that why you had it in a future?

theeternalpulse05:06:54

yeah, if I start it in a repl, it blocks input

theeternalpulse05:06:28

but running it as a future doesn't seem to work either. If I do something like (.stop @server) nothing happens

didibus05:06:16

Hum... what is run-jetty? I mean, do you expect it to return an object which has a .stop method on it?

theeternalpulse05:06:23

Yes, run-jetty is a function that should return the server instance

didibus05:06:52

Ok, well.. if it doesn't, something is strange

didibus05:06:23

Like (def server (run-jetty ...)) should return and not block?

didibus05:06:48

I think :join? false is important

didibus05:06:13

Did you try with that option, then you shouldn't need a future at all, and your thread shouldn't block

theeternalpulse05:06:31

ugh, I just saw that, very important it seems lol

didibus05:06:24

:rolling_on_the_floor_laughing:

didibus05:06:49

So, if you wanted that in all your repl sessions, you could put that line in your user.clj. Or in your run-dev-server alias as you were doing, and also add the nRepl code in there. So then when running that alias, you would get both nRepl and jetty started. You can then connect to nRepl, and access the jetty variable as normal.

theeternalpulse05:06:53

perfect, I think this and the user.clj would work like a charm

theeternalpulse05:06:25

thanks for the help!

theeternalpulse05:06:32

also you mentioned it's only for developement. So If I had an alias for the real server that called clojure.main and did an -e that ran the real server, user.clj wouldn't be called?

didibus05:06:31

That's a good question. I don't know if -e runs within user.clj

didibus05:06:07

If you use -m then it would skip user.clj, and unless you required it yourself, it wouldn't load

didibus05:06:54

Ok, it seems -e would also load user.clj

didibus05:06:56

What some people do is that, they don't bundle user.clj when they deploy their app. So it is only available in development,

theeternalpulse05:06:13

(ns user
  (:require [ :as app]))

(defonce server (app/run-dev-server))

(defn stop [] (.stop server))
(defn start [] (.start server))

theeternalpulse05:06:28

the user.clj worked fine

👍 4
didibus05:06:34

And others maybe don't care to have it bundled, but they bootstrap the prod Clojure app either with -m or some other custom launcher that doesn't load user.clj

theeternalpulse19:06:14

I ended up putting user.clj in a dev folder so I could do clj -A:nrepl:dev https://github.com/dantheobserver/parrot-api/blob/develop/deps.edn#L21 that way it's purely for a dev flow https://github.com/dantheobserver/parrot-api/blob/develop/dev/user.clj

didibus04:06:46

OH, good idea!

theeternalpulse05:06:16

yeah, I'll experiment with a "prod" alias and just use m for the server function

didibus05:06:51

Alright, glad I helped. I'm going to bed. GN

theeternalpulse05:06:50

night, thanks again

souenzzo11:06:29

Is it a #clojure-dev problem or a #liquid problem?

~ java --version
openjdk 12.0.1 2019-04-16
OpenJDK Runtime Environment (build 12.0.1+12)
OpenJDK 64-Bit Server VM (build 12.0.1+12, mixed mode)
~ clj -Sdeps '{:deps {mogenslund/liquid {:mvn/version "1.1.3"}}}' -m dk.salza.liq.core --jframe
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by clojure.lang.InjectedInvoker/0x0000000800231840 (file:/home/souenzzo/.m2/repository/org/clojure/clojure/1.10.1/clojure-1.10.1.jar) to method sun.java2d.SunGraphics2D.setFont(java.awt.Font)
WARNING: Please consider reporting this to the maintainers of clojure.lang.InjectedInvoker/0x0000000800231840
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

andy.fingerhut15:06:48

It has become an FAQ, if I am pattern matching correctly: https://clojure.org/guides/faq#illegal_access

orestis11:06:35

I’m struggling to figure proper use of the various io functions (input streams, output streams) to make sure I’m not doing anything silly or leaving things in memory etc. Here’s what I have (which is working):

(defn- save-workbook-gridfs [db workbook]
  (with-open [bas (java.io.ByteArrayOutputStream.)
              buf (io/output-stream bas)]
    (docjure/save-workbook-into-stream! buf workbook)
    (.flush buf)
    (gridfs-store-file db (.toByteArray bas)
                                   {:filename "report.xlsx"
                                    :metadata {:expires (expiration-time 1)}})))

orestis11:06:52

I need to create an OutputStream (`bas`) which I wrap into a BufferedOutputStream (`buf`) (not sure if necessary). Then I flush the buf to make sure the changes make it into bas, and then I get a byte array and pass that down to the thing that expects either a byte array or an InputStream.

aisamu12:06:51

Java's a bit rusty, but you might be able to get away without buf, since its buffer is another byte-array! You'd be avoiding repeatable small writes to a byte-array by writing to another (albeit smaller) byte-array

orestis12:06:59

Makes sense — I guess the buffering is needed when the main output stream is backed by a file or network or something like that, right?

aisamu15:06:36

Yup, something more expensive than the buffer! Doesn't even have to go that far... printing to a terminal can be surprisingly slow

orestis11:06:26

I believe everything is OK but it does seem complicated. I found the Piped* stuff but it seems like they might cause deadlock?

Suni Masuno14:06:55

When requiring things when is :as preferred and when is :refer preferred?

manutter5114:06:40

I almost always prefer :as because I’m a big fan of explicit code. I make a few exceptions for major, well-known stuff like deftest and core.async’s <!! etc.

clj 4
👍 12
lukasz14:06:01

Based on my experience - the only case when I use :refer is (:require [clojure.test :refer :all]) - aliasing imports or pulling in individual functions from namespaces gets confusing very quickly when working in a team, we tend to always alias a whole namespace and use it as -is (e.g. (:require [clojure.string :as s]) and then (s/replace ....))

jumar14:06:21

What are some of the best talks/resources about using Clojure macros? I heard @alexmiller to talk about the idea of "modeling domain as data, using functions to manipulate them and adding a thin layer of macro sugar " before (also mentioned here https://medium.com/@puredanger/hello-anders-just-a-few-points-to-consider-regarding-clojure-222cd0d7d4e7 ) I'm wondering which talks/resources do a really good job when it comes to explaining when/where/how to do that.

mrchance18:06:42

I really liked this talk: https://youtu.be/3yvrs9S0RIw

👍 4
sogaiu21:06:18

presume you meant "macros" -- don't know about best, but one thing i found helpful was: https://blog.brunobonacci.com/2015/04/19/dead-simple-introduction-to-clojure-macros/

jumar04:06:05

Yeah, sorry for the typo and thanks for the link 🙂. However, I was more interested in learning about real-world use cases and how to use them effectively rather than just explaining basic macro mechanics.

sogaiu12:06:05

ah, sorry, nothing comes to mind. would also be interested though :)

Alex Miller (Clojure team)15:06:16

The first chapter of Clojure Applied is about that. I’d probably change some of that now but still maybe useful

clj 8
jaihindhreddy15:06:15

^ While this is being discussed, I'll take this opportunity to thank Alex and Ben for the wonderfully tasteful book. Just finished reading my tree version. Is there a next version in the works with spec, socket REPL and possibly other stuff?

kszabo15:06:49

does anyone use tap> as a production logger? I’m afraid of the 1024 buffer size dropping log messages

ghadi15:06:16

that's off-label usage

ghadi15:06:38

it should be fine, as long as something is reading the taps

ghadi15:06:27

I hear implicitly in your question "we have a requirement not to drop anything"

Alex Miller (Clojure team)15:06:57

I would not use it as a production logger. it's intended for debugging.

Olical15:06:55

I use it instead of print in my prepl. Tangent: A shame it only takes one arg but makes sense, I usually end up passing a map with a bunch of values inside it. I have a vim macro to wrap the current form in a (doto ... tap>) which is really handy.

Alex Miller (Clojure team)15:06:32

We may add a tap-> that returns the passed value too

👍 16
Olical15:06:48

Discovering tap> returned true (which again, makes sense) was jarring the first time. tap-> would be neat! Totally get not adding it to avoid bloat though since there's ways around it.

kszabo09:06:52

I went ahead one more step:

(defn spytap [x]
  (tap> x)
  x)

(defn spytap> [x m]
  (tap> (assoc m :spytap>/value x)
  x))

(defn spytap>> [m x]
  (tap> (assoc m :spytap>>/value x)
  x))
This way you can associate any context (usually local binding) via a map in a threading.

borkdude18:06:58

@ghadi I forgot to reply in #graalvm and I see you just left. I think compiling clojure proper with GraalVM is quite ambitious, I would be surprised if that would work, but solving the 'locking' problem, so anyone doesn't run into that, is great.

ghadi18:06:16

there's a ton of other issues after fixing locking

ghadi18:06:24

1.10.1 has changes that barf in graalvm

borkdude18:06:08

@ghadi you were trying to "natify" clojure itself?

ghadi18:06:42

trying to compile the repro example on CLJ-1472

ghadi18:06:57

I left breadcrumbs

ghadi18:06:05

I no longer care about graalvm 🙂

ghadi18:06:12

err native image

ghadi18:06:24

It'd be nice if it worked out of the box, but I'm not going to expend too much effort trying to put a square peg (clojure- open-world assumption) in a round hole (native-image - closed-world assumption) @borkdude

borkdude18:06:09

I don't expect all clojure programs to work out of the box, but only a subset of clojure. and if it works, it's nice for command line applications

ghadi18:06:03

all applications working out of the box is impossible - I just mean clojure itself

ghadi18:06:20

which is still tricky

ghadi18:06:30

(post recent changes)

ghadi18:06:49

has anyone gotten graal native-image to compile Clojure 1.10.1 + with the locking change patch?

borkdude18:06:32

@ghadi do you maybe have a gist or something that describes how to build clojure + spec with this patch? I've tried it before and it was pretty involved, since spec itself is also AOT-ed

ghadi18:06:51

I forked it and added a deps.edn

ghadi18:06:01

one sec I'll find it

borkdude18:06:35

I have a branch of a project where I ran into this issue. I can retry it with your patch, once I know what steps to do

ghadi18:06:47

https://github.com/ghadishayban/clj1472 change deps.edn to point to the jar in the local directory and then call ./make

borkdude19:06:14

@ghadi I tested the 1.10.0 jar with the repro code to build a binary and I'm getting the locking error. Now I'm trying 1.10.1.jar. Do you have any steps where I can reproduce how you built those jars?

borkdude19:06:42

same problem with 1.10.1 jar

ghadi19:06:45

hmmm that doesn't seem right

ghadi19:06:57

are you using the ./make command or something different?

borkdude19:06:30

no, I'm testing your clojure.jar files to see if I can build a binary using the repro code, because that's what ultimately should work with the locking fix in place

ghadi19:06:45

if you want to transplant this elsewhere, you need clojure.jar + spec from my fork

borkdude19:06:46

I didn't succeed in executing make, because I'm not familiar with using the agent stuff from graalvm

borkdude19:06:19

you built those jars using a non-AOT-ed version of spec right?

borkdude19:06:30

that's what I'm curious about

ghadi19:06:33

right, see the deps.edn

ghadi19:06:35

unfortunately I can't devote any more time on this

borkdude19:06:01

thanks for giving it a try anyway

kszabo09:06:52

I went ahead one more step:

(defn spytap [x]
  (tap> x)
  x)

(defn spytap> [x m]
  (tap> (assoc m :spytap>/value x)
  x))

(defn spytap>> [m x]
  (tap> (assoc m :spytap>>/value x)
  x))
This way you can associate any context (usually local binding) via a map in a threading.