This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-06-29
Channels
- # announcements (10)
- # babashka (18)
- # beginners (136)
- # calva (9)
- # cider (14)
- # clara (12)
- # clj-kondo (32)
- # cljsrn (3)
- # clojure (133)
- # clojure-europe (21)
- # clojure-nl (4)
- # clojure-uk (15)
- # clojurescript (60)
- # conjure (40)
- # cursive (12)
- # datomic (6)
- # emacs (2)
- # fulcro (19)
- # jackdaw (25)
- # jobs-discuss (3)
- # kaocha (3)
- # leiningen (5)
- # off-topic (99)
- # pedestal (1)
- # re-frame (49)
- # reagent (4)
- # ring (5)
- # rum (5)
- # shadow-cljs (53)
- # spacemacs (2)
- # sql (13)
- # timbre (2)
- # tools-deps (23)
- # vim (11)
- # xtdb (7)
I’m looking at clojure.core.memoize and I am wondering, if multiple threads call memoized function with the same argument, does it guarantee that the underlying function is called once. It’s kinda hard to read what the thread safety guarantees are.
thanks
Indeed, I asked a very similar question before 😉 https://clojurians.slack.com/archives/C03S1KBA2/p1592570167141200?thread_ts=1592562954.132700&cid=C03S1KBA2
I'm trying to improve our log configuration, we are using timbre for logging. How can I route log to different appenders, should I write a custom middleware for dispatching or is there a built-in design for this case?
here you have a few examples of appenders https://github.com/ptaoussanis/timbre/tree/master/src/taoensso/timbre/appenders
We had a deep conversation about logging and to make it short we got to conclusion clojure.tools.logging
it today better, than timbre
.
Unfortunately this thread expire in slack and I can’t show this conversation.
another appender example for timbre
(timbre/merge-config!
{:appenders
{:sentry
{:enabled? true
:async? true
:min-level :info
:rate-limit nil
:output-fn :inherit
:fn (fn [{:keys [level ?err msg_ ?ns-str context]}]
(let [error-message (some-> ?err (.getLocalizedMessage))
causes (some-> ?err (ex-data) :causes)]
;(println "error-message" error-message)
;(println "causes" causes)
;(println "context" context)
;(println "msg_" (pr-str (not-empty (force msg_))))
(sentry/send-event (merge sentry-base
{:level (get timbre->sentry-levels level)
:fingerprint (when (or error-message (force msg_))
(->> (set [?ns-str error-message (force msg_)])
(clojure.set/union causes)
(remove #(= "" %))
(remove nil?)))
:logger ?ns-str
:extra context
:message (not-empty (force msg_))
:throwable ?err}))))}}})
hi, I want to configure to support specify one particular appender when logging, like specify a logger-name in logback, and that logger-name would dispatch to a few appenders.
looks like timbre only have the concept of appenders, so I should filtering in :fn
?
For example, I have three appenders, a
, b
, c
, I want to able to specify which appender to use when logging
(defn filter-appender [{:keys [vargs config] :as data}]
(if (keyword? (first vargs))
(let [appender-id (first vargs)
appender (get-in config [:appenders appender-id])]
(if appender
(assoc data :appenders {appender-id appender} :vargs (subvec vargs 1))
(throw (ex-info "Appender is not available" {:appender-id appender-id}))))
data))
At last, I wrote a middleware like this.just make some logic inside them. For example throw with ex-info
and check if there is :foo
keys with value X
.
I guess you can always read all appenders configured in timbre to throw an exception if there is no appender loaded
But the splitting can't be simply done via :ns-whitelist
and :ns-blacklist
. I'm working on a legacy project, and it use timbre everywhere. So I decide to stick with timbre instead of switching to logback + tools.logging. I know how to achieve this with logback.
in a deps.edn project with a git dependency, how can I access the readme of that project from code? (or can I?)
@auroraminor typically the README is not part of the source code (which is described in :paths
of the lib's deps.edn and defaults to src
), so you cannot access it from the JVM's classpath
What tools / libraries do you use for API ci/cd tests which are run outside of the system so simulate real chain of clients requests to API endpoints. So real HTTP requests like on production. The job which QA do manually.
https://www.pingdom.com/ maybe? Haven't used it against APIs, should be possible Pingdom itself has an api, so I can re-create setups as the bits rot
@borkdude what about in the scenario where it is in a code directory? (out of curiosity)
presuming only that some other lib does not define that resource, in which case you may or may not get the one you want (depends on classpath order)
yes, if a lib wants to expose that it's probably better to do it under an org+lib-named directory to avoid conflicts
yup, people sometimes don't think about resource location enough
ok, this is the way which I want to do, but I am curious if current IT world offer some tools which satisfy needs which I even don’t know about haha 😉
I think Apiary offered something in this area but it’s been a long time since I looked at it
I’d probably write my own tests using clj-http but if your system has first class API with potentially many external consumers it might be useful to look at alternatives
Hello, guys! I'm using Depstar to package an app. This app has a resource file under resource
folder.
I've configured a alias with 'extra-path' to tell Depstar to include that folder.
When depstar builds a uberjar, the resource/
content has been included on root of the .jar file instead of preserving the folder structure.
There is some way to change that?
if you include resource
as a path, and resource/foo/a
exists, you should have foo/a
in your zip
@ghadi Got it. Tks.
Hi folks, is there a way to read the forms from source code that handles aliased qualified keywords? I’ve tried with clojure.edn/read
and clojure.code/read
and get the same error. Thanks!
Execution error at: tool.core/-main ...
Invalid token: ::lib/key
src/example/input.clj
(ns example.core
(:require
[example.lib :as lib]))
(defn fn
[m]
(assoc m ::lib/key 'value))
src/example/core
(ns example.core
(:import
[ PushbackReader])
(:require
[clojure.edn :as edn]
[ :as io]
[clojure.pprint]))
(defn -main
[]
(let [source "./src/example/inut.clj"]
(with-open [r (-> source io/reader PushbackReader.)]
(clojure.pprint/pprint
(loop [forms []]
(let [form (edn/read r)]
(if form
(recur (conj forms form))
forms)))))))
edn/read will read the source that is following edn specification. ::lib/key
is not accepted https://github.com/edn-format/edn#keywords
aliases inside clojure.edn
is a strange combo - if it worked would you expect it to use the aliases of your current ns? a parameterizable one?
https://github.com/borkdude/edamame
there is configurable edn/clojure parser that should handle ::lib/key
Thanks @U04V4KLKC. I’ll give that a look.
auto-aliased keywords (the :: part) are not a feature of edn so edn readers won't handle it
the :: requires resolution in the "current" namespace, but edn does not have this concept
the Clojure reader, however, does support this, and even supports a pluggable namespace resolver
Thanks Alex. Yes, I found a few issues and one of your posts on Google Groups about that. I pivoted and tried using clojure.core/read with read-eval off too.
of course the simple solution is to fully type out the namespace instead of using :: - there's little downside
If it were a trivial example, I would. It’d involved a lot of code changes. 😕
How’d you find that Alex?
Living in Clojure? 😁
I worked on it :)
I have used it once or twice
Great memory! Thank you heaps, I’ll check that out.
Also, as a fanboy moment, thanks for Clojure Applied too. That was well structured and helped hit the ground running. 🤓
user=> (doc *reader-resolver*)
-------------------------
clojure.core/*reader-resolver*
nil
Are you spying on my REPL? I'm flattered @borkdude. 😁
(let [resolver (reify clojure.lang.LispReader$Resolver
(resolveAlias [_ sym]
(case sym foo 'foobar)))]
(binding [*reader-resolver* resolver]
(read-string "::foo/bar"))) ;;=> :foobar/bar
In a Clojure parser I made as a lib, this can be done like this:
user=> (edamame/parse-string "::foo/foo" {:auto-resolve {'foo 'foobar}})
:foobar/foo
Nice, that’s a lovely API. 👏
I’ve been trying to parse the AST on a ClojureScript project to track down how/when/where we use some third-party dependencies.
The macro expansion has been giving me some grief. Coming from JavaScript, it’s an interesting twist. The AST is expanded. 😆
I’ve naively assumed it’d be quick to read the forms and find out where we’ve used our def-ui-component
macro. That’s got those darned ::lib/key
keywords.
clj-kondo can be used for analysis and will give you a list of var references which might be easier to use than a tree
but ::lib/key isn't a var
or does it also do token tracking?
the root issue is :: resolution, so knowing about namespace aliases could be part of a solution
It does report this:
:namespace-usages [{:filename "<stdin>", :row 1, :col 21, :from dude, :to foobar, :alias foo}]
for (ns dude (:require [foobar :as foo]))
(let [resolver (reify clojure.lang.LispReader$Resolver
(resolveAlias [_ sym]
(case sym foo 'foobar)))]
(binding [*reader-resolver* resolver]
(read-string "::foo/bar"))) ;;=> :foobar/bar
That works! Neat.
for most projects, you could even hard-code ::foo to always be foobar
(and if not, the change that makes that work is a positive one and easy to do)
not sure if relevant, but: clj-kondo has a consistent-alias linter that will complain if you use different aliases for different libraries (you need to configure it for each lib that you want to have this check for)
It also could be at least partially automatic, right? i.e. one can query all aliases in a given project, and verify that there's a 1:1 relationship between libs and aliases (and not N:1, nor 1:N)
just throwing an idea btw, this also can be implemented quite trivially without clj-kondo (i.e. tools.reader)
rand-int can only go up to Integer/MAX_VALUE
yeah, you could use that, or call rand-int
twice and put one call into a bigdecimal and multiply by Long/MAX_VALUE, taking the sum
or - more precisely
ins)user=> (defn random-128bit [] (let [input-bytes (byte-array (repeatedly 16 #(rand-int Byte/MAX_VALUE)))] (java.math.BigInteger. input-bytes)))
#'user/random-128bit
(cmd)user=> (random-128bit)
86592240965117731739361063402086081396
(cmd)user=> (random-128bit)
89598420793514743141017230696508229384
(cmd)user=> (random-128bit)
129440819857997330997587888642583312139
(cmd)user=> (random-128bit)
149170186707650034165883028019004466810
(cmd)user=> (random-128bit)
116190502875785180875553570442774846270
you could easily parameterize this for how many bytes you want the value range to be
• edit to fix a bugwouldn't this be missing values since the range of byte is from Byte/MIN_VALUE to Byte/MAX_VALUE?
oh! you are right, I forgot that rand-int was positive only
right. so I think it would never generate a number like 511 (java.math.BigInteger. (byte-array [1 -1]))
yeah, lemme see if I can do this via an unchecked bit shift and bitwise and...
oh, just a shift needed
unsigned numbers are a pain in java
thanks for catching that issue, I think I have a better solution coming
(cmd)user=> (def frq (frequencies (repeatedly 10000 random-byte)))
#'user/frq
(cmd)user=> (into #{} (map val) frq)
#{59 20 27 39 46 54 48 50 31 32 40 33 22 36 41 43 29 44 28 51 25 34 23 47 35 45 53 26 38 30 52 42 37 49}
(cmd)user=> (apply min (keys frq))
-128
(ins)user=> (apply max (keys frq))
127
user=>
(import (java.math BigInteger))
(defn random-byte
[]
(-> Byte/MIN_VALUE
(* -2)
(rand-int)
(+ Byte/MIN_VALUE)))
(defn random-bigint
[byte-count]
(let [byte-list (repeatedly byte-count random-byte)]
(BigInteger. (byte-array byte-list))))
you may need to mask the very first bit to make sure the result isn't negative
I want a negative result
if only positive was OK, that would be much easier
I thought random-bigint
was supposed to have a positive result?
I mean the very first bit of the very first byte of the byte-list
oh - that part
cool, good idea
or you could just call .abs
on the big integer
I think I like this version better, easy enough to add a .abs
call if the domain requires it
(import (java.math BigInteger))
(defn signed-rand
[x]
(let [X (- (Math/abs (long x)))]
(-> X
(* -2)
(rand-int)
(+ X))))
(defn random-bigint
[byte-count]
(let [byte-list (repeatedly byte-count
#(signed-rand Byte/MIN_VALUE))]
(BigInteger. (byte-array byte-list))))
:thumbsup: . as long as it's clear that the range includes the negative side too
just divide the number of bits required by 8 of course
what a weird constructor https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html#BigInteger(int,%20int,%20java.util.Random)
never roll your own crypto, use a reputable library call which gives you what you want in one call
oh yeah, of course, rand-int
isn't a security feature and please don't pretend it is
trying to use a java library and running into the following:
(let [client (new OkHttpClient)
request (doto (new Request$Builder)
(.url "")
(.get)
(.addHeader "x-rapidapi-host" "")
(.addHeader "x-rapidapi-key" "")
(.build))]
(doto (.newCall client request)
(.execute)))
gives
class com.squareup.okhttp.Request$Builder cannot be cast to class
com.squareup.okhttp.Request (com.squareup.okhttp.Request$Builder and
com.squareup.okhttp.Request are in unnamed module of loader 'app')
any tips?
so you are throwing away the request built by the call to build and returning the builder
thanks seems right!
TIL java
..
might work here too since every step is a method call
something like (.. (Request$Builder.) (url ...) (get) ...)
sure! thanks
now that I've done this... I question why I used Java at all xD thanks anyway, will be useful later