This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-02-06
Channels
- # announcements (2)
- # architecture (2)
- # aws (18)
- # babashka (7)
- # beginners (149)
- # bristol-clojurians (4)
- # calva (11)
- # chlorine-clover (1)
- # cider (8)
- # clj-kondo (2)
- # cljdoc (2)
- # cljsrn (2)
- # clojure (186)
- # clojure-canada (3)
- # clojure-europe (3)
- # clojure-gamedev (5)
- # clojure-italy (1)
- # clojure-nl (13)
- # clojure-norway (4)
- # clojure-spec (25)
- # clojure-uk (32)
- # clojurescript (75)
- # core-async (2)
- # cursive (16)
- # data-science (3)
- # datomic (20)
- # docker (1)
- # emacs (26)
- # fulcro (7)
- # graphql (1)
- # incanter (1)
- # leiningen (1)
- # luminus (7)
- # malli (7)
- # mount (11)
- # off-topic (19)
- # pathom (15)
- # re-frame (9)
- # reagent (9)
- # remote-jobs (4)
- # ring-swagger (4)
- # shadow-cljs (63)
- # spacemacs (11)
- # sql (2)
- # vscode (7)
How do I configure USE_CONTEXT_CLASSLOADER
to be false
? I've got a Java file loading my Clojure code, however it fails to find clojure/core__init.class
. Doing some debugging, it looks like if that variable was false, it would use a classloader that is able to find the class.
How would I use that exactly? Clojure.var("clojure.core", "use-context-classloader")
?
It doesn't look like that has the bindRoot
function that RT.USE_CONTEXT_CLASSLOADER
has. (The latter doesn't work, because it tried to load clojure/core__init.class
before it's changed.)
Do I just set the variable, or is there a setting/function somewhere?
i have two methods that share the same options map argument, including the same defaults - i would like to extract the defaults, like so, but this doesn't work at all:
(defn some-fn-0 [{:keys [foo bar] :or {foo :you bar :me}}]
(prn "foo" foo "bar" bar))
(defn some-fn-1 [another-arg {:keys [foo bar] :or {foo :you bar :me}}]
(prn "foo" foo "bar" bar))
;; i want to refactor the above code like so:
(def default-options {foo :you bar :me})
(defn some-fn-0 [{:keys [foo bar] :or default-options}]
(prn "foo" foo "bar" bar))
(defn some-fn-1 [another-arg {:keys [foo bar] :or default-options}]
(prn "foo" foo "bar" bar))
i tried :or ~default-options
as a shot in the dark, but that didn't work either. i'd appreciate any advice.you can't do this
the destructuring options map is a literal syntax
another way to approach this is to not do it in destructuring
but instead merge
the incoming map into a default map
(def default-options {:foo :you :bar :me})
(defn some-fn-0 [m]
(let [{:keys [foo bar]} (merge default-options m)]
(prn "foo" foo "bar" bar)))
Curious, is there such a thing as uberjar
ing without compiling .clj files at all? (neither mine or transitive)
I'm mostly intrested in uberjar as means of bundling dependencies + resources, but otherwise I don't particularly want AOT. I'd favor a slower deployment over running into AOT oddities
I think I've run into AOT issues in production only once (at most), but anyway they are an occasional pain point in #clojure so I think I'd be happy to kill the mere possibility of these issues
With a previous client I regularly built non-aot'd uberjars, mostly because they were being deployed as storm topologies and pomtemkin (a dependency) was causing some odd behaviour post-aot. One option is to include a java shim in your source that requires your namespace and calls the main fn. Set that java shim as the main class in the manifest, e.g. :manifest {"Main-Class" "some..java.Main"}
import clojure.java.api.Clojure;
import clojure.lang.IFn;
public class Main {
public static void main(String[] args) {
IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("your.system"));
Clojure.var("your.system", "start!").invoke();
}
}
Yes, you can make a non aot uberjar
Iām just talking conceptually, they are independent things
yeah, it did occur to me to merge the default-options map inside of the function body. thanks alex!
hello everyone
Hi alex, how u doing?
pretty good
People, I'm having an issue here regarding aws lambda converting a map to json
here's my method signature:
[^{:static true} [getValue [java.util.Map] clojure.lang.IPersistentMap]]
When i'm handling an exception, i return a map that look's like this:
{::a/category ::a/incorrect
::a/message error-msg
:exception-data response}
This :exception-data
key is a map response from clj-http, and it's type is: clojure.lang.PersistentHashMap
Here is the error message from aws lambda:
An error occurred during JSON serialization of response: java.lang.RuntimeException
java.lang.RuntimeException: An error occurred during JSON serialization of response
Caused by: .UncheckedIOException: com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.UnsupportedOperationException) (through reference chain: clojure.lang.PersistentArrayMap[":exception-data"]->clojure.lang.PersistentHashMap[":http-client"]->org.apache.http.impl.client.InternalHttpClient["params"])
Caused by: com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.UnsupportedOperationException) (through reference chain: clojure.lang.PersistentArrayMap[":exception-data"]->clojure.lang.PersistentHashMap[":http-client"]->org.apache.http.impl.client.InternalHttpClient["params"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:177)
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:199)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:683)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:561)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:469)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:29)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:561)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:469)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:29)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:130)
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1387)
at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:1088)
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:926)
Caused by: java.lang.UnsupportedOperationException
at org.apache.http.impl.client.InternalHttpClient.getParams(InternalHttpClient.java:211)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:654)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
... 11 more
and btw @borkdude i'm using clj-kondo
and it helps me a lot! Thanks for this š
wait! i think it might be this that come's inside :exception-data
:http-client #object[org.apache.http.impl.client.InternalHttpClient 0x55917c8d "[email protected]"]
i think this jackson
lib that aws uses is unable to parse this into JSON
yes! exactly this
just dissoc'ed from the map and it worked
Has anyone done fake HTTP basic auth for clj-http-fake
@grounded_sage basic auth is just a http header, how would you like to fake it/
I just want to return a 403 status when the headers arenāt right.
Though I canāt seem to see how to use this lib properly. https://github.com/myfreeweb/clj-http-fake
Even when I provide it incorrect form-params it also matches against anything.
Looks like it ends up in body but itās unclear how to catch that without doing it in the return function
Iām just doing it in the return function instead
There's #:: {:_/key "value"}
syntax. But I can't find anything about the bogus _
namespace in the documentation. Is it undocumented for some reason? If not, could someone please give me a link?
https://clojure.org/reference/reader under "Map namespace syntax"
I'm curious. Is this still a thing? https://groups.google.com/forum/#!topic/clojure/abpXQBiCjNo
is what still a thing?
keywords not being garbage collected. I'm running some tests on visualvm but it seems it's being garbage collected fine. the code I'm running: https://gist.github.com/caioaao/08fc6ed2b5e45cd146514a4f8e4257b7
keywords are now stored in a weak table as weak refs so they can be gc'd but still cached
symbols can't be cached since two of the "same" symbol could have different metadata stored
cool. I was afraid of using the usual json parsers that keywordize stuff from wire because of that thread š
@caio this is where it happens https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Keyword.java#L41
at least from the script I used to test it seemed fine. I was generating random 10-char strings and keywordizing it
I think the problem is if you have 10,000 char strings
(for example)
if it was genuinely untrusted input, you could exhaust memory pretty quickly by just generating a lot of really long random strings and sending them over to be keywordised ... that table is going to grow quickly, unless it's doing something clever
It's not just the memory. It's a hash table. Meaning, if you know the hash function and you control the input, you can make it much more slower than it usually is.
@U2FRKM4TW huh, thanks for that anyway. TIL hash flood attacks :) https://www.freecodecamp.org/news/hash-table-attack-8e4371fc5261/
also, keywordizing json blindly still isn't always the best idea - json allows keys that are nonsensical for keywords, and I'd only keywordize if you need to eg. cross reference with map literals
yeah. keywordizing json only plays a role here to work with clojure.spec/spec-tools, since we don't have an easy way of defining a keys
spec for string keys. I'd rather have the keys as strings btw, and was considering using plumatic schema for API contracts, but content negotiation (like reitit does) is good too and it gets a bit more messy if I go the string-as-key route
How do I pass in a function to a macro someone else has defined? Then have it evaluated?
Do I have to wrap it in another macro?
macros are transforming a source form to a new source form
if they pass the name of a function in as a symbol, you can return the same symbol in your output, that's about all you can do
I am using clj-http-fake and wanted to conditionally return statuses based on parameters passed. But I canāt seem to do figure out how I can pass a function into the with-fake-routes macro
hmm, something like (defn foo [f] (with-fake-routes {some-route f} ....))
should actually work - maybe you're doing something weirder?
the macro gets f, and returns f, and the function makes sure f is the right value in the resulting code
Itās an asynchronous clj-http request
So perhaps I need to do a .get on it since it returns a future?
For now I just have a deftest for each case but it could all be modelled as conditionals inside of with-fake-routes return function
yeah. keywordizing json only plays a role here to work with clojure.spec/spec-tools, since we don't have an easy way of defining a keys
spec for string keys. I'd rather have the keys as strings btw, and was considering using plumatic schema for API contracts, but content negotiation (like reitit does) is good too and it gets a bit more messy if I go the string-as-key route
This doesnt seem to work:
(ns example
(:gen-class
:name test.override
:methods [ ^{Override {}} [toString [] String]]
:state state
:init init
:constructors {[String] []}))
(defn -init
[name_]
[[] (atom name_)])
(defn -toString [this]
(deref (.state this)))
I get a Caused by: java.lang.ClassFormatError: Duplicate method name "toString" with signature "()Ljava.lang.String;" in class file test/override
@caio @ghadi btw this is a helper function I use to look at clojure internals - it can usually take you straight to the gh page for an object from clojure.core
(defn ghdoc
"looks up some java in the clojure repo, based on the `javadoc` function"
[class-or-object]
(let [target (if (class? class-or-object)
class-or-object
(class class-or-object))
target-str (string/replace (pr-str target) #"\." "/")
url (str " " target-str ".java")]
(browse/browse-url url)))
where browse is clojure.java.browse and string is clojure.stringI'm playing with the idea of reading feature flags in and defining them as state with mount ... and am running into: "Don't know how to create ISeq from: clojure.lang.Symbol class java.lang.IllegalArgumentException" when calling (defflags [{:flag :test :value "val"}]). I'm not familiar enough with macro's yet to see where I've screwed this up. Any help appreciated š
@activeghost this problem could come up if you called deffflags with the name of a vector instead of a vector literal
you could use (doseq [{flag# :flag value# :value} ~flagsmap] ....)` except you need to generate the defstate calls
might be easier with another macro that takes a keyword and generates a defstate call
Thanks @noisesmith will try your suggestion and composing macros and see what I can get. It is a vector name, unquoting it in the for expression gives me an attempt to call unbound fn (which at least is different :)).
Just to follow up .. this macro works for defs. I don't actually need to define these as states since they aren't stateful .. just compile time data , and since attempting to do so with `(defstate (symbol (name flag)) :start value) doesn't work just moving over to defs ... defstate isn't seeing the :start keyword (have unquoted it, quoted it, etc. ...still doesn't register).
the original you showed didn't provide :start at all
a keyword is self-evaluating
user=> (= :a `:a ':a (keyword "a"))
true
also
user=> (symbol :a)
a
(symbol (name x)) usually isn't needed since symbol knows what to do with keywords alreadythat's true. IMHO there isn't much reason to write new code for older versions, but that is a concern
Yes, had left that out in the original (important, but realized that later). Thx, simplifies it.
I'm trying to enable Jetty's gzip compression by copying https://martintrojer.github.io/clojure/2015/10/04/enable-gzip-with-ring-and-jetty. Large responses don't contain Content-Encoding: gzip in the response headers. I'm using ring-jetty-adapter 1.8.0. Any idea if something has changed?
is the content type in the list and is the content greater than the min size you set?
Ah, had to enable debug logs. This appears to be it:
2020-02-06 02:12:01 qtp1212997867-539 [org.eclipse.jetty.server.handler.gzip.GzipHandler] DEBUG - [email protected]{STARTED,min=100,inflate=-1} excluded by method Request(POST //localhost:8880/api/v1/eql)@3e5cfa48
Is there a reason to exclude certain HTTP methods from gzip? Off the top of my head, it seems ok to enable across all methods.
After adding (.setIncludedMethods (into-array ["GET" "POST"]))
in, it worked as expected.
from the vieiwpoint of the REST spec, GET and maybe POST make sense because they return arbitrary data, the others either have minimal result or aren't normally used for documents
Jetty also lets you set (.setMinGzipSize)
to whatever you want. You cannot set it below the gzip breakeven point. For those methods that return small responses, gzip would be skipped.
Iām calling a function inside a deftest
that is in another namespace and does a swap on an atom. I am using that to keep some state.. is there any way to redfine that and have it scoped lexically or is it trapped holding onto the one on the other namespace?
you should be able to with-redefs
the atom in the namespace
It doesnāt appear to be working
(deftest confirm-entity
(with-redefs [failed-requests (atom 0)]
(let [response (fn [id] (with-global-fake-routes {{:address (:confirm-changed-entity url-list)}
(fn [request]
(let [form-params (slurp (:body request))]
(cond
(= "Identifier=8" form-params) {:status 200}
(not (= "Identifier=8" form-params)) {:status 500})))}
(confirm-entity! id)))]
(is (= :success (response 8)))
(is (and (= :failed (response 5)) (= 1 @failed-requests))))))
(defn confirm-entity!
[id]
(http/post (:confirm-changed-entity url-list)
{:async? true
;:oncancel #(println "Confirming entity " id " was cancelled")
:accept :json
:basic-auth auth
:form-params {"Identifier" id}}
(fn [_response] :success)
(fn [exception]
(swap! failed-requests inc)
(println "failed requests " @failed-requests)
(println "exception message is: " (.getMessage exception))
:failed)))
It works as expected with (defn confirm-entity! ā¦)
is in the same namespace.
probably need to prefix with the namespace the atom is in
(require '[project.core :as some-ns])
(with-redefs [some-ns/my-atom (atom nil)] ...)
Iāve tried that as well š
or you can use the fully qualified namespace
with-derefs will replace the atom with a different atom, it won't change the value of the exiting atom
best thing to do is not def mutable things, but pass them in as arguments where needed
I was trying to keep my code clean. But all these exceptions have made me have to do some annoying stuff and now with testing I have to do even more š
the async? true in your http request will also play havoc with dynamicly scoped things like with-redefs
e.g. your http request is put a threadpool to be executed when the with-redef is in effect, and then execution leaves the scope of the with-redef, then the http request is actually executed
async has made my code a mess lol. Though I donāt know how else to handle this scenario
(not sure what http client library you are using or what :async? true actually does)
you have to ensure that with-redefs remains in effect until after the http request has been made
it allows you to catch the return of the request in a callback. Returns a future or something. I donāt quite understand it that much.
so you need to stop, and wait inside the scope of the with-redef, until that future/callback/what is resolved
This testing library I think uses a macro to test the http request. So it doesnāt escape??
I get that code needs to be testable. But the modifications being required I think are a bit unjustified at this point.
you can do stuff and make this work, but you have to understand what you are doing, and it is easier to understand things if you are directly passing arguments and call functions then it is to understand macros and how dynamic scope interacts with multiple threads
it isn't really about making it testable, it is about making it understandable as a decomposition of parts instead of having to understand the entire thing at once.
you seem to think what you have now is easier to understand, but I would point out if you understood it you would be able to write a test for it, which suggests maybe re-evaluating what you have
Iāve got one thread that gets a list of entities to fetch and feeds another thread that acts as a queue (using core.async channels). They throttle making requests to a server that canāt handle excessive load. Then I have async on http-clj so I donāt block on that waiting for the response.
Iām writing tests now because Iām having to build up a series of exceptions to handle a volatile API.
I've had coworkers write tools to do things like mocking out http requests for that kind of thing, but I have never found it particular useful, if I want to test out making http requests or handling http requests I start up an http server and make requests of it
Yea Iām thinking this is the way to go.
Maybe I just reset! the atom in the core namespace during the test..
I mean that works but seems a little wrong lol
opinion: when you have (is thing "some message")
generally should the message describe what you expected or should it describe what you assume went wrong i the assertion failed?
eg. the docstring has Example: (is (= 4 (+ 2 2)) \"Two plus two should be 4\")
- would you translate that hypthetically to "Oops 2 + 2 wasn't 4" or "2 + 2 = 4" (if you had to pick one)
I was surprised not to see a recommendation about this in the Clojure style guide
I translate "Two plus two should be 4" in a failing test to "The test writer expected two plus two to be 4, and checked for that, but the condition was false"
I brought up the translation in order to attempt to "drive a wedge" into "describe the problem" vs. "describe the expectation" because I think that example walks the line between the two
I have written tests such that when they fail, they print out more details about actual vs. expected results, especially when those actual and expected values are collections, as an aid to starting to debug what went wrong.
it could be the right answer is c) it doesn't matter, either way is fine
I mean, the problem is "I got results I didn't expect". Trying to guess what that might have gone wrong could be done, but how good that guess is really depends upon how much you know about the implementation, and what might have gone wrong.
yeah, the concrete circumstance is a PR where a bunch of is
clauses try to describe the failures, and I'm deciding whether it's worth pushing back on haha
That guess could be spot on, or if the implementation has changed significantly since when the test was written with those guesses, and the tests were not also updated to match, the guesses could be way off.
Such tests could be interpreted as over-specifying or over-constraining the solutions, I suppose.
If a development team is willing to write them, but not willing to update them if/when the underlying implementation changes, then they have created tests that will confuse new team members, for sure.
I have written functions that check conditions on the internal structure of trees like Clojure's PersistentVector data structure (the Java internal implementation details, not just at the API level) when trying to look for bugs in the core.rrb-vector library. If I or someone else changes the internal implementation details, but not those internal-structure-checking functions, the results could be anywhere from slightly to majorly confusing and wrong.
right - "white-box" testing of that sort is a conditional tech debt that kicks in if the implementation is refactored
I knew that, eyes open before hand, when I did it, because if I didn't write those functions, I would have missed many opportunities for finding actual bugs. It also helped me learn some invariants of those data structures that I didn't know going in. If I had illusions that those checker functions would somehow be eternally matching a changing implementation, well, they would be just that -- illusions.
yeah, I've done the same - tested things that are really implementation details because it made it easier to write a correct implementation
This may sound like a leap in the conversation, but somehow it reminds me of this slide title in one of Rich Hickey's talks "Programming is an economic activity" (this talk -- https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/ClojureMadeSimple.md ). I didn't realize that fully 30 years ago, but I have gradually realized it over time. Lots of arguments over development practices are between people who realize this versus those who do not, or between people who have different guesstimates of which practices will have different ROI
Where ROI isn't always measured in money, but can more generally be regarded as happiness/satisfaction/effectiveness versus time spent to achieve them.
this kind of thing really tips me off into the deep end contemplating "the industry", my profession, and my life in general I guess. On the one hand, of course programming is an economic activity, but on the other hand the incentives are almost universally bad. code delivered quickly and cheaply is almost universally rewarded regardless of the long term effects
No big argument from me there. I would counter with: have you ever seen software developers that want to rewrite something from scratch, when that is a terrible economic decision?
perhaps more often from someone who joined a project later, versus earlier, but also from people who were there from the beginning. In many cases, sure, there are bad technical decisions made in haste, but sometimes it is just a desire to achieve some Platonic ideal of code goodness, d*mn the time/cost to get there.
Often based upon lots of new information learned, or changed requirements, in between those two times. It isn't always "wow, I was lazy then".
the sort of interface between programming and business, and in particular how programmers communicate and justify what they do to their managers is, I think, kind of where the rubber meets the road on this kind of thing, and always interesting
or rather, it isn't always "wow, I was really cutting corners then"
I think it is well understood by experienced development managers that lots of engineers are bad at guesstimating how long projects will take. There are other reasons, but at least part of the idea of doing "sprints" is based upon not making people estimate much further than 2 to 4 weeks in advance š
I would connect this to a kind of distinction between what a programmer produces (code) vs. what is experienced by others including those tasked with managing programmers (the effect of the code in the world, uis drawn on screens, emails sent, etc, the artifact).
yeah, beyond timing stuff, just how what is being done and what needs to be done is communicated
(sprints will spin me out in a whole other direction about scrum and consultants, planning poker is wild)
Sorry, not trying to trigger a drawn out discussion on agile practices / hype / consultants, but at least one simple observation is: if someone isn't yet good at estimating how long things take, making them practice it over and over again every 2 weeks is likely to improve their ability to estimate how much they can do in 2 weeks, at least, versus only making them do it every 6 months.
except estimation (from scrum for example) explicitly does not estimate time periods
it estimates "complexity", which, speaking of economic activity, the business doesn't really care about, they care about time periods
> but sometimes it is just a desire to achieve some Platonic ideal of code goodness, d*mn the time/cost to get there. I have a pet theory that this is perfectly rational. "A good codebase" is more immediately an asset for the developers than for their managers. Lack of technical debt means you can make a feature and go home at five.