This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-06-06
Channels
- # announcements (1)
- # beginners (147)
- # boot (9)
- # calva (28)
- # cider (3)
- # circleci (18)
- # cljdoc (54)
- # cljs-dev (55)
- # cljsrn (22)
- # clojure (179)
- # clojure-canada (9)
- # clojure-dev (74)
- # clojure-europe (1)
- # clojure-italy (15)
- # clojure-nl (7)
- # clojure-spec (30)
- # clojure-uk (55)
- # clojurescript (65)
- # core-async (15)
- # cursive (12)
- # datomic (16)
- # events (4)
- # fulcro (25)
- # graalvm (3)
- # joker (2)
- # kaocha (15)
- # keechma (94)
- # off-topic (12)
- # qlkit (2)
- # re-frame (15)
- # reagent (11)
- # reitit (29)
- # remote-jobs (15)
- # rewrite-clj (16)
- # shadow-cljs (73)
- # spacemacs (144)
- # sql (3)
- # tools-deps (11)
- # unrepl (19)
- # vim (35)
@matt.henley Hum... it's not always wrong, but in your case it is. I'd say you want to map each components to a namespace, think component diagram in UML.
Also, your tasklist is a poor's man data-structure and a remnant of you trying to do OOP. It doesn't need to be encapsulated behind an interface.
You could spec it to help people understand its structure and work with it more effectively if you really wanted.
For your tasks, you could move them to be more like a state map with transitions. Like adding a status key: {:status #{:open :completed}}
So now you don't need a completed? isOpen? IsPrending? Etc. You can just call :status as a function
And I would personally go all in with the state machine. And I'd create a transitions map which lists valid transition from a status to others like:
(def transitions
{:open [:completed :pending]
:completed []
:pending [:completed]})
Now I would accept transition commands, and I would multi-spec them over the :status key
(defmulti t (fn[task new-state] [(:status task) new-state]))
(defmethod t [:open :close] [task new-state]
(-> task
(assoc :status new-state)
(assoc :date-completed (. js/Date now))))
(t {:status :open} :close)
;> {:status :close,
:date-completed 1559792294514}
Finally, I'd move all that in a component which would be my only namespace for it all. Maybe I name it taskscheduler.cljs
Internally, it would make new tasks by calling your create and adding them to the tasklists namespace global Var. It would check for the status by just checking the :status key on a task. It would transition status with the multi-method, throwing an exception if no valid transition from/to exists. It could return the valid transitions from the transitions map for a given task if you need to list them out in the UI for example.
I realize now maybe your tasks were more for a todo app 😋. I was definitely thinking scheduled jobs in my head.
Then I'd just shove it all in say a todo.clj namespace. And I'd make it all simpler like:
(def lists (atom {}))
(defn new-list [name]
...)
(defn add-task [list-name item]
...)
(defn close-task [list-name task-idx]
...)
Etc.
What are options for browser automation from ClojureScript?
I am trying to use websockets with sente. When I try to curl the endpoint i get this error message:
Client's Ring request doesn't have a client id. Does your server have the necessary keyword Ring middleware (`wrap-params` & `wrap-keyword-params`)?*
and a stack trace in the server that says the same thing. I am just following the example outlined on the project's homepage, https://github.com/ptaoussanis/sente#getting-started. this gist has the server and the curl command and output: https://gist.github.com/jldoubleu/70dbe2eb6d31ae8a5c36730d748e0fc0i guess curling isn't really the problem. i just realized in the gist i wasn't passing client-id query param
the actually problem that i was trying to reduce to the simplest case is that the clojurescript client is getting erroring out 403 when trying to establish the connection
403 Bad CSRF token. which tells me what is wrong, but i am not really sure how to resolve it. For this example i do not really care about csrf
@justin.williams.1181 lucky you 😄 i spent a lot on this few months ago 😄 and this is the answer https://github.com/ptaoussanis/sente/issues/339#issuecomment-478187728
ok, i set the csrf-token-fn to nil in the call to make-channel-socket! and that seems to have resolved my issue for the test case
yeah that's about it if you ever need a token just look at sente example it will give you a hint on how to do it. (render home.html on backend with csrf and then use that value for communication)
@lepistane yeah thanks that is what i figured out to work around. which example is that?
Hi! I'm trying to import classes from inside an external jar file but I'm having zero success - feel like I'm missing something obvious (it's my first foray into interop), would anyone be able to give me some pointers? 🙂
btw. this is a useful resource: https://clojure.org/reference/java_interop
I've built an app around SAP's Java Connector ("sapjco3.jar"). I've tried two ways to import classes from it, both of which work fine in the REPL and during uberjar build, but cause issues at runtime. The first is adding a :resource-paths entry to my projects.clj, which I assume is the correct method but causes the JVM to fail to start ("Java Virtual Machine Launcher: A Java Exception has occurred.") The second is extracting the contents of the jar to my resources directory, which works(with limitations.
Since I can't repackage SAP's code due to licensing restrictions, I assume I need to keep sapjco3.jar outside of my uberjar and reference it at runtime?
Otherwise you should probably install it into your own maven repo or at least the local repository
Ok, I've tried installing the jars to my local repository, but SAP's jars check the file name at runtime to prevent repackaging; since mvn install renames them to "sapjco3-version.jar", and doesn't seem to respect -DstripVersion=true, I'm not sure if this route will work
(JCo initialization failed with java.lang.ExceptionInInitializerError: Illegal JCo archive "sapjco3-3.0.19.jar". It is not allowed to rename or repackage the original archive "sapjco3.jar".)
(On reflection, even if there's a technical workaround I think adding it as a dependency would count as repackaging SAP's code and therefore isn't viable)
clojure is a java library, use the java classpath config to add the jar when starting clojure
you don't need to use maven or include it in an uberjar - just ensure that it's in a reliably findable place on disk, and add it to your classpath on startup
noisesmith, that makes sense, but I can't import classes even though (.exists (File. "sapjco3.jar")) returns true, which would suggest that it's already on the classpath. I'm not sure how I'd declare this explicitly in projects.clj due to this change: https://github.com/technomancy/leiningen/wiki/Repeatability#free-floating-jars
And then to ensure repeatability of your build process you probably need to install it somewhere. You don't have private nexus or alternative?
@UAH1JFQV9 this seems relevant to your problem: https://support.mulesoft.com/s/article/How-to-use-Maven-to-add-SAP-Connector-dependencies
> (.exists (File. "sapjco3.jar")) returns true, which would suggest that it's already on the classpath this is not how classpath works, the current directory usually isn't on classpath
as @U06BE1L6T says you use the -cp
arg to java to set classpath
you can use lein install
or an equivalent mvn
command to make an arbitrary jar usable via maven, or hard code any path you like if you want to make a special case
Hi jumar, noisesmith, will experiment with -cp and see where I get to. I guess I need to configure my IDE to add the jars to the classpath on REPL launch, then run my uberjar'd application from e.g. a batch file that references both the uberjar and the SAP jar? Thanks for all your advice so far!
that's likely the simplest option, yeah
Hi all, a very beginner question, how does a function return something which manipulates a lazy map? a println works inside the func, but if I call it from outside it doesn't show anything
@comparalf I'm not sure if I'm reading that correctly, but if you call map inside the function you either need to realize the elements needed before you exit, or return the lazy-seq so something else can realize it
if you don't realize an element, and don't return the seq containing it, the containing sequence gets garbage collected without realizing it
user=> ,((fn [] (map println (range 1000)) nil))
nil
Uhm.. So, I think the question is now, how do you convert the map to something no-lazy? materialize it..
or use run!
or doseq
instead of map if you weren't using the data
That said, it's not deep. So if you have lazy things of lazy things, you need to call doall
on all of them.
Ya, also mapv
, reduce
and loop
can be used instead for eager processing which return a useful value
And when you feel like you've mastered all that, transducers can also be used for eager processing. But that's more complicated so maybe don't start there.
Ok ok, I think I got the basic idea, I'll look for those functions, thanks to both
So I'm new-ish to clojure (at least at scale) and picking up someone else's codebase. If I had a bunch of "what's the prettiest way to write this?" questions which channel should I direct that at?
I consumed the style guide, but I'm trying for better than the minimum. ^_^
Thanks! ^_^ So which is preferred...
(get-in data [0 :someKey])
(:someKey (first data))
or is there some clever difference between them?
So the second version is more data agnostic?
I'd use either (:some-key (first data))
or (-> data first :some-key)
depending on what the surrounding code looked like.
(I agree with @hiredman that using get
/`get-in` to navigate sequences is something I would not use)
If it helps this is a vector of... is map the right word in clojure for {:a 2}
?. And a vector is still officially a seq right?
Honestly the data in here looks like json with {}/[] nestings
List => seq and vector as peers then?
>.< Sorry, clearly got a lot left to learn
So if in my earlier example I preface with...
(def data [{someKey 12} {someKey 22}])
Does that change the correct answer?assuming I want 12
I think the issue is that using get/get-in restricts your function to only working with vectors instead of sequences in general
Making the other way more data agnostic?
yeah, it could come back to bite you later on if you pass eg. the result of a map
or filter
to it
That makes sense. If the pathing down starts to get pretty long is there a better option, or should I vec=>get-in to get the same behavior?
Ok, I can see how to do it that way. I'm coming from js/ramda https://ramdajs.com/0.19.1/docs/#path and I suppose that warped my perception here a bit. Still learning to think more clojure-y
I don't think there's anything wrong with using get-in
here if you know that all the data structures you're going to be traversing are associative, and both maps and vectors are.
Ohhh, new concept for me here, what exactly does associative mean? (linked article would be fine if more convenient)
🙇 thank you!
@suni_masuno Clojure data structures are said to be 'associative' if the implement the clojure.lang.Associative
interface.
In a map the associative relationships are straightforward: Each key is associated with its corresponding value.
You can test if a data structure is associative with clojure.core/associative?
:
(associative? [])
true
(associative? {})
true
(associative? #{})
false
That... is a super helpful answer. ^_^ You should write one of those tiny super helpful medium posts I'm always coming across with just that.
As others were pointing out above, many functions in clojure.core
that consume collections will output sequences regardless of the type of data that was provided as input. For example:
(associative? [1 2 3])
true
(map inc [1 2 3])
(2 3 4)
(associative? (map inc [1 2 3]))
false
@U050CT4HR so, only Lists and Sets are Seqable?
@UCMNZLJ93 Nope! Being associative and being seqable are not mutually exclusive. If you seq
a vector you get a sequence of the vector's values. If you seq
a map you get a sequence of key/value pairs.
So, when you make a collection seqable does it retains associative properties?
@UCMNZLJ93 If you're talking about seq
, seq
doesn't "make a collection seqable", it turns the collection into a sequence. Sequences are not associative.
@U050CT4HR what is difference between seq
and seqable?
@UCMNZLJ93 seq
is a function in Clojure core. Seqable
is a Java interface that collections implement.
Hoping out of the thread, get or get-in
are introducing a requirement of associative, while arguably being a bit simpler/more readable depending on taste?
The thing I like about get-in
in this case relative to, say, ->
is that it's easier to scan (makes the intent more explicit), particularly as …
gets long. For instance, if I see (get-in x …)
even before looking at …
I already know that all we're doing is accessing data within x
. By contrast, if I see (-> x …)
I need to read each item in …
to understand whether we're accessing data, transforming data, triggering side effects, or some combination of all three.
This is an instance of the more general principle of using the smallest / most limited tool for the job.
@alexmiller Yeah, that's more the pattern I'm used to in Ramda too where we're handing around paths near as much as data (ok, like 20% as much, but still) That said the much greater diversity of data representations in clojure makes get-in significantly less safe than path is in JS
A point I didn't know before this channel!
@suni_masuno Something else to be aware of
user=> (get-in [{:some-key 12} {:some-key 22}] [2 :some-key])
nil
user=> (-> [{:some-key 12} {:some-key 22}] (nth 2) :some-key)
Execution error (IndexOutOfBoundsException) at user/eval37656 (REPL:4).
null
user=>
get
/`get-in` will yield nil
when the element can't be found but nth
will throw an exception. However: user=> (get-in (list {:some-key 12} {:some-key 22}) [2 :some-key])
nil
user=> (get-in (list {:some-key 12} {:some-key 22}) [1 :some-key])
nil
user=> (-> (list {:some-key 12} {:some-key 22}) (nth 2) :some-key)
Execution error (IndexOutOfBoundsException) at user/eval37662 (REPL:7).
null
user=> (-> (list {:some-key 12} {:some-key 22}) (nth 1) :some-key)
22
user=>
Note how get-in
produces nil
even for an index that is in bounds here -- because a list is not associative -- but you can use nth
on a list although it will be O(n) -- linear.Not sure if this is the correct channel. regarding a clojurescript question: I started a new project with figwheel and it was running correctly my reload function, css changes make my project reload as well. But when i started with figwheel-main tools and lein trampoline i just loose theses features. Probably i may didnt understand my config with my profiles settings. Someone here can help me?
@papachan do you have the figwheel-main.edn file?
I did the same thing, migrate from to figwheel to figwheel-main, I remember to change my aliases in project.clj
:aliases {"fig" ["run" "-m" "figwheel.main"]
"fig:build" ["run" "-m" "figwheel.main" "-b" "dev" "-r"]
"fig:min" ["run" "-m" "figwheel.main" "-O" "advanced" "-bo" "dev"]
"fig:test" ["run" "-m" "figwheel.main" "-co" "test.cljs.edn" "-m" condo.test-runner]
I've just tested de fig:build.. but it works so I think at least, it'll run
And I added [com.bhauman/figwheel-main "0.1.9"] for dependencies in dev profile
here there are my aliases settings:
:aliases { "fig" ["trampoline" "run" "-m" "figwheel.main"]
"fig:dev" ["trampoline" "run" "-m" "figwheel.main" "-b" "dev" "-r"]}
I had not never seen trampoline, you could try give a shot without it, using the same parameters that fig:build
Uhm, if the problem is with the css files, in the project.clj
:resource-paths ["resources"]
with figwheel i had this, i suppose its not valid anymore when i move to figwheel-main
^{:watch-dirs ["test/cljs" "src/cljs" "src/cljc"]
:css-dirs ["resources/public/css"]
:auto-testing true
:log-level :error}
I hope so.. haahahah
That's my dev.cljs.edn
Perfect
my last question is how i can use again this reload function: :figwheel {:on-jsload "clojurescript-app.core/on-js-reload"
wouldn't that just be adding an :on-jsload
key to the dev.cljs.edn?
maybe it's too much to hope it would be that simple