This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-02-26
Channels
- # announcements (4)
- # beginners (160)
- # boot (2)
- # calva (40)
- # cider (41)
- # clara (24)
- # cljdoc (2)
- # cljs-dev (99)
- # clojars (4)
- # clojure (71)
- # clojure-dev (9)
- # clojure-europe (4)
- # clojure-italy (2)
- # clojure-nl (19)
- # clojure-spec (97)
- # clojure-uk (103)
- # clojurescript (57)
- # core-logic (1)
- # cursive (15)
- # data-science (31)
- # datomic (24)
- # duct (1)
- # emacs (39)
- # events (7)
- # figwheel-main (14)
- # fulcro (44)
- # garden (7)
- # jobs (13)
- # juxt (1)
- # leiningen (29)
- # music (2)
- # nyc (4)
- # off-topic (37)
- # pathom (12)
- # re-frame (26)
- # ring (1)
- # ring-swagger (10)
- # shadow-cljs (35)
- # spacemacs (2)
- # specter (4)
- # test-check (67)
Hi! I was wondering if there is a way to mock Java classes (constructors, etc) for testing in Clojure. The only solution I've found is to wrap whatever I'm doing in a function and mocking that function, but that doesn't feel quite right.
I'm specifically trying to mock (Socket. host port)
from java.net.Socket
. 😮
@pberganza10 If the function you are testing took a socket as an argument, instead of creating it, then you could pass in a "mocked" socket using reify
.
If your function constructs Java classes and then uses them, it's going to be very tightly coupled to those classes, making it harder to test (as you've found).
@seancorfield Yes, that's basically what it's doing. So, basically, I should change my approach and separate Java as much as I can from my functions if I want to make my functions testable?
It's more about separating construction from use. Whenever you have a function that "constructs" something, that function is closely tied to the thing it constructs. By having functions that "use" things be passed in the thing that they use, you separate the construction part and make it easier to test functions because you can pass thing different versions of those "things".
Does that make sense @pberganza10? (Sorry, for the slow response, I was listening to an episode of NPR's "Live From Here" because Amanda Palmer was on it)
It does make sense. And no worries at all! Thanks a lot! I'll take that into account going forward
Hello guys, What is the idiomatic way of writing something like: (defn myfunction [something something-else] (let [x1 (transient something)] (let [x2 (assoc! x1 :somewhere (some-function (something-else (x1 :some-value))))] (let [x3 (my-function-that-assoc! something-else (another-function (x2 :another-value)))] (let [x4 (my-funtion-that-conj! something-else-again (x3 :test))])))) (persistent! x4))
Is there a way to get rid of the nested let in this case?
@borkdude it is not the point. It won't work. It is a cascaded let
x3 depends on x2. x2 depends on x1, etc...
😲 let me try
In this case my example above would be:
(defn myfunction [something something-else] (let [x1 (transient something), x2 (assoc! x1 :somewhere (some-function (something-else (x1 :some-value)))), x3 (my-function-that-assoc! something-else (another-function (x2 :another-value))), x4 (my-funtion-that-conj! something-else-again (x3 :test)), (persistent! x4)]))
you need to have a closing square bracket before (persistent! x4)
if you want that as the return value
sure!
For some reason Sublime does not like it:
It does not like the commas.
Is there any issue if I reassign to the same variable like this:
(defn myfunction [something something-else] (let [something (transient something) something (assoc! something :somewhere (some-function (something-else (something :some-value)))) something (my-function-that-assoc! something-else (another-function (something :another-value))) something (my-funtion-that-conj! something-else-again (something :test))] (persistent! something)))
You can do this but it's not idiomatic in most cases.
The threading macros ->
and ->>
help here.
Also check out destructuring. It can be used in all places where symbols are bound (except binding
)
@rodrigo759 It will work totally fine, let simply rebinds
the value
great! Thanks my dear clojurians friends!
Hi, I have an integration test that utilize use-fixtures
to start and stop com.stuartsierra/component
system that I have. Let's say the app is supporting multiple dbs, in short:
;; system.clj
(defn new-db
[{:keys [kind] :as config}]
(case kind
:datomic (new-datomic config)
:postgresql (new-postgresql config)
:mongodb (new-mongodb config)))
(defn new-system
[config]
(c/system-map
:db (new-db (:db config))))
;; test_fixtures.clj
(defstate system
(atom nil))
(defn system-fixtures
[f]
(let [config (-> "config.edn" io/resource slurp read-string)]
(reset! system (c/start (new-system config)))
(f)
(swap! system c/stop)))
;; app_test.clj
(use-fixtures :each system-fixtures)
(deftest some-test
;; tests here...
)
Any ideas on how to tell the fixtures to read from another config file? Currently I have multiple file with same namespaces and functions, and load the ns with which one I want to use.@rodrigo759 > It does not like the commas. Commas are optional in Clojure.
The code below is not guaranteed to work because it does not use the result of conj!. But I am a little bit lost on how to replace the for to a more idiomatic construct. What is the idiomatic way of doing it?
(defn myfunction [bean-object] (let [transient-obj (transient (process bean-object))] (doall (for [[k v] bean-object] (conj! transient-obj {k v}))) (println (transient-obj :somethig)) (persistent! transient-obj)))
By the way... It does work on my computer... But it is not guaranteed.
What is the problem with persistent! ?
I think you can reduce your code to something like:
(into (process bean-object) bean-object)
:thinking_face: ok. I will try. Thanks!
What is the problem with persistent! ?
you're using transients as if they were mutable objects (which they are, but not always)
so one correct way to write your code from above would be
(defn myfunction [bean-object]
(let [transient-obj (transient! (process bean-object))
transient-obj (reduce (fn [transient-obj [k v]] (conj! transient-obj {k v})) transient-obj bean-object)]
(persistent! transient-obj))
yes. that's what I am fixing in my code now.
I am still struggling to understand how to use into to replace my original for construction.
if you're using transients directly, which there's often not very good reasons to, think of them as if they were immutable objects
ok. I am very concerned about performance. In my case that bean-object is a huge map. The code I showed is the simplification of my real code. It has a lot of processing on that map. I am using transients hoping that it will give me a better performance. It is part of an optimization AI routine. Every millisecond is important for me.
:thinking_face: Ok! Still trying to convert my original for
code to something using into
@rodrigo759 the expression I posted is effectively the body of your function
Go it! Tks!
I really really want to work out how to use zippers in anger, and I came across a case where they seemed potentially useful at work. Couldn't figure out the rules for implementing them though.
'({::case/situation ["The login page"], ::case/expect "can be reached"},
{::case/situation ["The login page" "when incorrect credentials are entered"], ::case/expect "rejects the login attempt"},
{::case/situation ["The login page" "when correct credentials are entered"], ::case/expect "logs the user in"},
{::case/situation ["The login page" "when correct credentials are entered"], ::case/expect "greets the user"},
{::case/situation ["The login page" "when correct credentials are entered" "and the user is new"], ::case/expect "offers the tutorial"},
{::case/situation ["The users page"] ::case/expect "has a Users header"})
The above is the result of what I named a story
function which acts on a mess of test output
The longer the ::case/situation vector, the more complicated the setup before the test checks its assertion
This looks like a tree to me --- "The login page" and "The users page" are the children of an unlabeled root node, "The login page" has one leaf child and 2 branch children, etc
(defn branch?
"If there are no more precondition statements (or no precondition at all, if that can even happen),
then the node cannot be a branch."
[story]
(when-let [pre (::case/precondition story)]
(not (empty? pre))))
(defn children
[stories]
(vals (group-by (comp first ::case/precondition) stories)))
These seem pretty close... Although maybe for this children function to work, it would have to remove the bit of the ::case/situation that we've seen in the parent
But I really struggled on the make-node
function --- I made some attempts but I don't really know what it means so I was just thrashing around
Does it...
• Get the label given a node? That can't be right, it takes a sequence of 'new children' (which doesn't super make sense to me in this situation but neither did branch?
at first and I got there eventually)
• Given a label and some children "build up" a higher bit of the tree? That might make sense if zippers can infer how to travel down but they need help to travel up.
• ???
Shout-out to @plexus for a great explanation of why zippers are extremely cool! But I'm not quite making that last jump...
It's twofold: - learn to implement a zipper when something looks treeful - "zoom in" on the test data by complexity of story. So for instance, zip down to the situation, "The login page when correct credentials are entered" and return that zoomed-in bit of the results
see, I thought that the z/zipper
function was for, You clearly see that your data could be a tree so here's the functions that would make it into one so it can be navigated that way
zipper
is more about creating a structure that allows you to move within the tree, transforming it to a tree would be a separate step before it IMO
I ended up with a couple of functions that would update a precondition by dropping the first element, or concatenating a higher precondition onto 'em
do you think, then, the right way is to transform the given structure into something that is actually nested?
otherwise you’re going to be doing two things at once: transform it AND making it zipper-able which would make my brain hurt
also, the major benefit of zippers is that it allows you to transform the tree “in-place”
if you don’t need that capability, you might find that the new nav
stuff is actually a better fit
which means.... implement datafy
? I'm nervous about that 'cause I haven't worked with metadata
like, if all you want to do is traverse the tree and return a specific branch then nav
will be much easier
you might not need to use datafy
, if you’re already working with Clojure data and you don’t need to do anything fancy
riddle me this: if you had this in a tree structure already, would you be able to do what you want to do with get-in
?
Dunno. Probably? Goal questions are things like, "What's the widest node, let's get that one and then examine the children of that wide node one by one"
When I am actually squinting at this data, I am looking at the widest collection of failures, cause sometimes if I fix that, the most complicated stories (the ones with the longest preconditions) will end up fixed as a side effect
And since I'm looking to prioritize more than change, that certainly sounds get-in
-y
so maybe there’s some filtering that you would want to do, in order to figure out which nodes are the best candidates
ah yeah, then I would transform it into an actual tree organized by situation
to make that easier
Alright --- so my key misunderstanding was, it's not that zippers make tree-like data trees, it's that they make tree data navigable
And specter
one more clarification: nav/datafy
, would they also require me to organize this into a real tree first?
But at some point you just gotta write that reduce or loop-recur to transform it into a tree
I've a small workflow question. I've the following program:
clojure
(ns socketserver.core
(:require
[aleph.tcp :as tcp]
[manifold.stream :as s])
(:gen-class))
(defn handler [s info]
(s/connect s s)
(s/put! s "Hello! I'm the socket server :-)"))
(defn -main [& args]
(tcp/start-server handler {:port 1678}))
I would expect that after I've run (-main)
, change the handler string and reevaluate the handler in CIDER, that the handler would now return the new string. However I still get the old message 😕.Hi fellas, this mind-boggling error is consuming me Can't make API call *METHOD* in a thread that is neither the original request thread nor a thread created by ThreadManager
@patrickwinter I've not looked into it too deeply but I had a similar case recently, I think it's an issue where where the the value of the var get's passed down but when you update the var that doesn't change the value that got passed to your start-server
function
The hacky fix I used was to replace the reference in your -main
function with something like #(apply (var-get #'handler) %&)
Very hacky I admit, but I couldn't figure out a better way to fix it quickly
Ah, does that work? Nice
@#'handler
, you mean?
Thanks for your help. @#'handler
does not seem to work.
when you write a name like handler
if the compiler doesn't find a local in scope with that name, it resolves it to a var and compiles it as a deref of that var
so (foo handler)
compiles to something like "deref the var handler, and use it as an argument when invoking foo"
Thanks for the explanation!
and vars proxy function calls to their values, so if a var contains a function, you can invoke the var like a function
Earlier this morning I was wrestling with zippers before turning my data into trees first, and I'm realizing I have a very informal understanding of a tree.
- It is a graph without cycles
- The nodes may have labels
- The nodes may have children
- (branch? node) => true
iff the node could have children, otherwise (leaf? node) => true
Since I'm coming from graph theory rather than computering, are there bits of the protocol that seem obvious that I'm leaving out?
zippers also require a function that returns a seq of nodes and function that given a seq of nodes returns a new node
I would start with https://github.com/aysylu/loom
i'm not sure --- i don't need to do graph theory, i am just interested in zooming in and out of a collection that has a tree structure
i mean, my experience is from graph theory, so i feel like there's a chance there's a bunch of obvious stuff that i'm overlooking because we never stored data in our trees in school
This is the kind of data I start with--- https://clojurians.slack.com/archives/C053AK3F9/p1551196822322200
but since branch / leaf / children are functions, you could define a tree an an adjacency list
and i can squint at it and see the tree without it literally having that nesting structure
but also direct operations on an adjacency list are easier than using the zipper abstraction in many cases...
because they aren't mutable, so you have to pull them apart on the way down, and build them back up with the change in place on the way back up
thank you both. I am very interested in getting good at working with trees or things-that-can-be-transformed-to-trees, and I will look at those. (Has someone implemented lenses or would I be reading papers?)
I should add, there is additional data in those maps I posted --- so I've been trying to think for a while about navigating with those sequences of strings, in order to obtain the entire zoomed-in collection of the maps
having played with zippers a bit for doing crazy macro rewrites, I've never reached for zippers for real stuff
in general because if you hand me a deeply nested structure the first thing I do is flatten it out
there is a straightforward algorithm for building a tree from an adjacency list that meets the structure requirements (sort from leaf up and nest onto parents), and what you show above seems not to be far from being an adjacency-list
zippers are for trees in the sense that any nested datastructure is a tree, they are not some generalized library for trees
but I agree that usually the flattened form is more useful