This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-04-27
Channels
- # aws (19)
- # babashka (47)
- # beginners (111)
- # boot (3)
- # bristol-clojurians (3)
- # chlorine-clover (2)
- # cider (13)
- # cljs-dev (8)
- # clojure (143)
- # clojure-europe (11)
- # clojure-germany (10)
- # clojure-italy (3)
- # clojure-losangeles (1)
- # clojure-nl (1)
- # clojure-spec (6)
- # clojure-survey (3)
- # clojure-uk (42)
- # clojurescript (229)
- # conjure (131)
- # cursive (21)
- # data-science (18)
- # datomic (4)
- # emacs (21)
- # events (2)
- # figwheel-main (12)
- # fulcro (18)
- # graalvm (1)
- # hoplon (40)
- # jobs (1)
- # joker (17)
- # kaocha (1)
- # lambdaisland (1)
- # off-topic (19)
- # rdf (7)
- # re-frame (31)
- # reagent (26)
- # reitit (20)
- # rum (4)
- # shadow-cljs (106)
- # sql (17)
- # testing (5)
- # vim (2)
Hi! Is there a way to add metada to a final Java class? I need to have different cheshire
formatters for different instances of a Java class (backwards compatibility).
You can't add metadata to arbitrary Java objects. Only to things that implement clojure.lang.IObj
@seancorfield Thanks for the explanation. I've tried using proxy
but that didn't work.
(defn wrap-money
([^Money m] (wrap-money m {}))
([^Money m _meta]
(proxy [Money IObj] [(.getAmount m)]
(meta [] _meta)
(withMeta [meta'] (wrap-money m meta')))))
Even if I create a Java wrapper that implements IObj, it's not gonna work because the Money
class is final.
All you could do is use composition, make something take a Money inside itself. MoneyWrapper, you can make that a vector, a map, a record, or go for a deftype or a Java class, up to you
But I'm guessing maybe cheshire has another mechanism to pick its formatter, aside from meta?
@didibus I've spent the last hour reading the cheshire docs & source and, to be honest, I think even the meta route wasn't going to work because I don't think Cheshire will pickup the correct superclass.
I really don't want to have to refactor old code, so I've been trying the polymorphism/meta route before a wrapper container.
Clojure protocols can add functionality to existing classes, I believe even final ones, only for Clojure protocol functions. Not sure whether Cheshire has any protocols you can extend in its implementation
@andy.fingerhut cheshire does have protocols to extend its implementation, but I don't see how I would differentiate one format from another without metada or somehow extending/subclassing the original class. Maybe my OO is a little rusty?
Can you create a wrapper/box class for the special instances and have it contain the money object, then add an encoder for your box class that accesses it?
(require '[cheshire.core :as json]
'[cheshire.generate :as cg])
(defrecord MyMoney [money])
(cg/add-encoder MyMoney
(fn [c jsonGenerator]
(.writeString jsonGenerator (str (.-money c)))))
(json/generate-string {:m1 (MyMoney. 123)})
;;=> "{\"m1\":\"123\"}"
(just replace the str
call in the encoder to however ou want to access the money object)Thanks for the suggestion, that's precisely where I arrived at, was just coming back to report on that 😃
the fact that proxy
(understandably) doesn't support final classes crushed my simple workaround, but this is a reasonable alternative.
@U0B7P8YMB I haven't personally done it, but I believe that one of the reasons for creating Clojure protocols is so that you can define protocol functions differently for every class, even for final classes and/or ones that you do not want to change/extend in any way.
https://clojure.org/reference/protocols. in particular the part about extend
Using defn
, there is no way to attach metadata on the fn itself rather than the var, is there?
the only metadata that is attached to the fn is :tag but you can use
(def foo (with-meta (fn [x] ...) {:k 'v}))
defn is just a macro that expand into ^ that formIndeed, but it would have been slightly nicer keeping defn
.
Thanks!
write your own macro )
(defmacro defn* [meta & body]
(let [[_ name f] (macroexpand (cons 'defn body))]
(list 'def name (list 'with-meta f meta))))
These will break arglists in most tooling... I'd recommend copying the meta to the fn
tooling should be ok because arglists attached to var not to the function
hey everyone, Im seeking general advice regarding writing tests and fixtures
lets say Im testing a REST API, specifically a GET /user/123
where 123
is the id of the test user create right before the test by a fixture
(defn create-delete-user [f]
(fixture/create-user)
(f)
(fixture/delete-user))
(use-fixtures :each create-delete-user)
(deftest user-test
(testing "get user bla bla .."
here I need to compare the result of the HTTP call with the fixture user))
lets say fixture/create-user
returns the created user, whats the best way to share this data with the test inside the testing block? I could create an empty atom and write and read there, but this just feels wrongyou could use a binding instead if you didn't want to use an atom:
(def ^:dynamic *user* nil)
(use-fixtures :each
(fn [test]
(with-bindings {#'*user* (fixture/create-user)}
(test)
(fixture/delete-user)))
which one is more idiomatic clojure? Im ok with anything, Im just asking because of shared mutable state etc
the idiomatic thing would be to use binding
- my understanding was that with-bindings
is a helper for macros
i always forget which func is which, for some reason my eyes gravitated towards with-bindings
this time 😛
imo binding does feel more idiomatic. however, assuming test runs are independent, i typically prefer the "fixtures create/clear tables, while tests setup their data explicitly" route
I could also dont use a fixture to create data and do stuff inside a let block on the test itself, but it feels dirty
thats exactly what I am talking about, to be honest seems the most sane way of doing it
although it feels strange to mix setup code with the actual test, but maybe Im just used to something else and this is fine
some pedestal docs I stumbled across yesterday approached this with a purpose-built macro
(defmacro with-system
[[bound-var binding-expr] & body]
`(let [~bound-var (component/start ~binding-expr)]
(try
~@body
(finally
(component/stop ~bound-var)))))
you still end up with a let-like syntax in your test, but at least you don’t have to copy the setup/teardown
(use-fixtures :once
(fn [test]
(db/create-schema!)
(test)
;; really only needed if your db instance is persistent
(db/drop-schema!)))
(use-fixtures :each
(fn [test]
(db/clear-tables!)
(test)))
(deftest test-something
(let [user (db/crete-test-user!)]
(sut/do-a-thing user)))
imo it all really depends on how much your tests share, how complex the db is, etc
Is it possible to get an ISO String formatted date in Clojure without using Java? ie is there a built in for clojure? (iso string):
"2017-06-09T06:59:40.829-00:00"
I don't think Clojure core has a date time API. Just use the one from Java 8 (not the calendar API, use the Local* things).
well, I figured I'd ask anyway, i was having a hard time finding anything in clojure. Thanks
also if clojure had a built in for time, that's what it would do
with few exceptions any domain stuff will not be bundled with clojure itself, only data structure and algorithmic functions are well represented
(of course it uses java.time
underneath
@U011PQGUDCG
(str (.toInstant #inst"2000"))
second that. also see my lib https://github.com/henryw374/time-literals to improve the experience with data literals, e.g. #time/instant "2018-07-25T07:10:05.861Z"
If I start a thing in the REPL that is supposed to read from STDIN, how do I send something to it?
by sending something to stdin - or more generally you can bind *in*
to an apropriate source of input
clojure's read
etc. will use whatever *in*
is set to (it can be set dynamically using binding
)
when you use a network repl client, *in*
will be bound such that read
still works (unless other tooling like your editor gets in the way)
of course, be wary of calling read
in a background thread while accepting input on the same *in*
in the main thread (and visa versa)
Interactivity would be nice, but it's okay if I have to bind *in*
to something. Since there is only one Java process now (the REPL's) I was confused who will capture the input.
Your answer helps! Thanks.
you don't have to bind *in*
if you want to use the same input source as the repl itself, it will just work (unless an editor breaks that...)
the *in*
of your repl client is the one that will be used by read
, not the one of the server process
how did you start the server? it might be possible to get its stdin, it might not, but it shouldn't matter
That still means I have to dump something in a text file and bind that to *in*
. Is there another way?
if you want to read from a text file, that's pretty much how you do it - unless you want to use slurp
and read-string
instead of read
that's why I mentioned editors
thanks to cider jack in, emacs owns your server's real stdin and I doubt you'll get it back
What difference would it make in the case of cider-connect?
because emacs started the process and connected something to it
maybe there's a way to ask emacs for that file handle and redirect something else to it, but that's not a clojure question any more, it's an emacs / unix question
and I don't expect it to be easy - not compared to binding *in*
which already does what one needs here
It seems Emacs can send something to an inferior process and rebind its stdout on the fly to a buffer. But I will experiment with it some other day. (Bottom line: Emacs does everything!)
except for example threads
Emacs is a mini operating system
without threads
Emacs does threads already. https://nullprogram.com/blog/2018/05/31/
I stand corrected
I wonder if this is up to date "https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual quickly shows that Emacs’ threading implementation has many data races, making it https://hboehm.info/boehm-hotpar11.pdf."
"only one thread runs at a time"
anyway, this is well off topic by now, sorry
NP. Don't know. Never bothered. I like Emacs because you can be totally off the mouse and some of the things just work. Although the UI is terribly slow as compared to something like VSCode.
@U051SS2EU what alternatives do you suggest to Emacs?
I don't think that's on topic here, I meant the thread thing to be a joke / tease but my knowledge is out of date
use emacs if that's the thing that works for you
Threads in emacs are experimental for now. Packages are advised not to rely on them yet
but you shouldn't need it - nothing in clojure relies on the identity of stdin itself, it all uses the *in*
abstraction instead
Is there a secret way for aliases to include other aliases in deps ?
what is your goal? running multiple commands or combining multiple pieces of configuration?
Rather combining multiple pieces of configurations (dependencies, really)
and invoking multiple aliases at the same time is just more than you want to require of project users?
just trying to understand the needs
It is just that sometimes (but not that often, I concede) it can become a bit hard to track, what alias should be invoked when
Honestly it is not a big deal, I was curious about it but I would certainly not fight for it...
we have some new stuff coming soon and so I'm really trying to tease apart whether this would be helped or could be helped
@adam678 No. Aliases cannot bundle together other aliases. It's quite a common request.
Might it be fairly straightforward to write a Clojure program that reads a deps.edn file, or something similar to one that introduces some new convention for data that lets one alias reference another one, and merge the contents of the aliases together? Seems like a potentially nifty 3rd-party utility, although definitely understandable if Clojure core team would not want to write/maintain such a thing.
That would open the door to N different tools (or perhaps one that combines N different features) for reading something, and generating a deps.edn file.
Just for info, is it by choice or by constraint? Otherwise yes, custom bash scripts is what I am doing, but it does feel too "smart". Maybe it is for the best...
Also, while you can't have an alias of alias, you can list out multiple alias when running clj to combine them
Oh yeah, by custom bash scripts this is what I meant, so that I don't have to type long chains of aliases combinaisons
I'd like to run clojurescript tests using https://github.com/lambdaisland/kaocha-cljs kaocha-cljs. So I put the [lambdaisland/kaocha-cljs "0.0-71"] dependency in my lein project.clj, and restart the repl and run the command in the root directory:
clojure -m kaocha.runner unit-cljs
But I get the error:
Execution error (FileNotFoundException) at clojure.main/main (main.java:40).
Could not locate kaocha/runner__init.class, kaocha/runner.clj or kaocha/runner.cljc on classpath.
Why would this be, and how can I fix this?clojure doesn't use project.clj
you probably want the koacha lein plugin, assuming such a thing exists
why does kaocha-cljs exist then if you could just use kaocha for running cljs tests?
never mind, you are using koacha-cljs not koacha, and that doesn't use lein
it looks like you would need to migrate away from lein to use koacha-cljs, or port it to lein somehow
so I'm using the Luminus template, and I haven't yet needed to worry about the connection between cljs and clojure. But any cljs dependency I put in the project.clj dependencies and it just works.
sure, that's how the lein integration works
but koacha-cljs is built around clj/clojure which is a different tool, not compatible with lein
@souenzzo that gives me this error:
.BindException: Address already in use
at .bind0(Native Method)
at .bind(Net.java:433)
at .bind(Net.java:425)
at .ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
at io.netty.channel.socket.nio.NioServerSocketChannel.doBind(NioServerSocketChannel.java:128)
at io.netty.channel.AbstractChannel$AbstractUnsafe.bind(AbstractChannel.java:558)
at io.netty.channel.DefaultChannelPipeline$HeadContext.bind(DefaultChannelPipeline.java:1358)
at io.netty.channel.AbstractChannelHandlerContext.invokeBind(AbstractChannelHandlerContext.java:501)
at io.netty.channel.AbstractChannelHandlerContext.bind(AbstractChannelHandlerContext.java:486)
at io.netty.channel.DefaultChannelPipeline.bind(DefaultChannelPipeline.java:1019)
at io.netty.channel.AbstractChannel.bind(AbstractChannel.java:254)
at io.netty.bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:366)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:465)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
Syntax error (BindException) compiling at (/private/var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/form-init6496307984697349116.clj:1:125).
Address already in use
Full report at:
/var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/clojure-9061200669573262320.edn
and I also tried in the clj repl:
(require '[kaocha.runner])
nil
user> (kaocha.runner unit-cljs)
Syntax error (ClassNotFoundException) compiling at (*cider-repl luminus/vendo:localhost:63551(clj)*:62:7).
the lein command line would translate to (koacha.runner/-main "unit-cljs")
Still, I get 1. Unhandled java.lang.ClassNotFoundException koacha.runner URLClassLoader.java: 382 http://java.net.URLClassLoader/findClass DynamicClassLoader.java: 69 clojure.lang.DynamicClassLoader/findClass ClassLoader.java: 418 java.lang.ClassLoader/loadClass DynamicClassLoader.java: 77 clojure.lang.DynamicClassLoader/loadClass ClassLoader.java: 351 java.lang.ClassLoader/loadClass Class.java: -2 java.lang.Class/forName0 Class.java: 348 java.lang.Class/forName RT.java: 2211 clojure.lang.RT/classForName RT.java: 2224 clojure.lang.RT/classForNameNonLoading Compiler.java: 1041 clojure.lang.Compiler$HostExpr/maybeClass Compiler.java: 7045 clojure.lang.Compiler/macroexpand1 Compiler.java: 7075 clojure.lang.Compiler/macroexpand Compiler.java: 7161 clojure.lang.Compiler/eval Compiler.java: 7132 clojure.lang.Compiler/eval core.clj: 3214 clojure.core/eval core.clj: 3210 clojure.core/eval interruptible_eval.clj: 91 nrepl.middleware.interruptible-eval/evaluate/fn main.clj: 437 clojure.main/repl/read-eval-print/fn main.clj: 437 clojure.main/repl/read-eval-print main.clj: 458 clojure.main/repl/fn main.clj: 458 clojure.main/repl main.clj: 368 clojure.main/repl RestFn.java: 137 clojure.lang.RestFn/applyTo core.clj: 665 clojure.core/apply core.clj: 660 clojure.core/apply regrow.clj: 18 refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn RestFn.java: 1523 clojure.lang.RestFn/invoke interruptible_eval.clj: 84 nrepl.middleware.interruptible-eval/evaluate interruptible_eval.clj: 56 nrepl.middleware.interruptible-eval/evaluate interruptible_eval.clj: 155 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn AFn.java: 22 clojure.lang.AFn/run session.clj: 190 nrepl.middleware.session/session-exec/main-loop/fn session.clj: 189 nrepl.middleware.session/session-exec/main-loop AFn.java: 22 clojure.lang.AFn/run Thread.java: 748 java.lang.Thread/run
you still need to require koacha.runner
, and it needs to be available as a dep you can require
that error is saying it hasn't been loaded
user> (require 'kaocha.runner) nil user> (koacha.runner/-main "unit-cljs") Execution error (ClassNotFoundException) at http://java.net.URLClassLoader/findClass (URLClassLoader.java:382). koacha.runner