@factorhengineering clojure(script) has to work with the platform tools. So your html's script tag has to point to the js file location, shadow-cljs is a build tool helped to provide some helper functions in organizing that. Basically every dev is going to want to do the same thing and with a library like shadow we can all be on the same page: reusable functionality, shared mindspace, optimizations, etc... So i would say its definitely a shadow cljs question. But the shadow docs are really good. Don't hesitate to ask me questions though š
I think I ended up starting in the deep end of the pool with: https://shadow-cljs.github.io/docs/UsersGuide.html While I was away from my computer and had a few minutes with my phone i found this: https://github.com/shadow-cljs/quickstart-browser ...kind of wish I had stumbled in that first. Ha. Going to go through that now.
@drewverlee Can you shed any light on the 20MB (and 200ish folders and 1000ish files) installed in a fresh project folder by npx create-cljs-project my-project
(as specified in the User Guide at the 1st link above)?
...whereas git cloning the quickstart folder+file set (as specified in the Quickstart at the 2nd link above) yields 5 folders and 7 files?
Is the first, heavy installation just trying to guess at many, many libraries that a person might want?
Oh...finishing the installation for the quick guide project actually results in another massive thing, slightly larger than the user guide one even. I guess my question about what the point of all those file is still stands, though.
Npx is a way to localize Js, it puts it all on your directory, rather then globally. This is better for the obvious reasons. That's likely what your seeing.
Well, I shouldn't say obvious, but I'm on my phone.
OK. Maybe it will make more sense to me soon. š
The files on your computer are used to build the files that will be sent to the browser, you don't have to be to concerned about the formers size.
There are many steps to reduce size between the two.
This isn't clojure ecosystem thing, it's more of a sensible way to handle deps. It's main stream, nearly every language does some version of it. Then again in assuming that's the issue, without seeing them files on question I might be leading you astray.
Well in the first case, I'm not sure if the create-cljs-project
part of the npx
command is linked to shadow-cljs, but it seems like it's a pretty generic thing, so I did guess maybe everyone's JS or at least CLJS project folders start huge like that.
@factorhengineering Are you talking about the node_modules
folder?
Yeah, you are, I just tried that command. Think of node_modules
as the āMaven repositoryā of the JS world ā go look in ~/.m2/repository
for what Maven (and Leiningen/Clojure CLI) has installed on your system! š
That folders contains over 23,000 files for me in about 8,000 directories for me ā it positively dwarfs what npm
/`npx` lays down in node_modules
! š
@seancorfield Yes, the node_modules folder.
Just ignore it. Like I say, itās the āMaven repoā of the JS world.
I just took a peek in ~/.m2/repository
folder. Yes..lots of stuff. Actually, I'm happy to see cider
and hiccup
there. Both are things I thought I might want but would need separate installations later.
Itās all just libraries ā in both the JS world and the Java world. You donāt really āinstallā stuff in the Java world: the tools you use to run your code (or build your artifacts) automatically download the dependencies they need.
package.json
is the deps.edn
of the JS world.
You anticipated a question I was about to ask. Thanks. š
...and I guess if I'm using shadow-cljs, I'm going to be manipulating (maybe not manually but still...) shadow-cljs.edn
instead of deps.edn
?
Well, shadow-cljs.edn
is all about the front end stuff youād be doing with Shadow-cljs. If you also build a backend piece for the app, youāll need deps.edn
(or project.clj
) for the Clojure part.
Shadow-cljs is kind of its own āislandā. But itās an island that is very friendly to npm stuff if thatās what you need.
Aha. But can't back-end stuff also be done with shadow-cljs? I thought the genesis of Node.js was for back-end stuff, and shadow-cljs seems pretty linked with node.
Shadow is for producing JS. There are a lot of full stack frameworks in clojure. Try luminous, or fulcro. Both have options to use shadow. Your time learning shadow will pay off, it's widely adopted.
So usable for server-side, but not without some other...what's the word...tooling?
(like Luminus or Fulcro)
I was going to try Luminus as my starting point, by the way (and did run into mention of Fulcro as well while trying to wrap my head around all this before actually setting anything up), but I was advised that might not be the best place to start.
Maybe soon, though! I thought I could use shadow-cljs (and nothing more) to make a desktop app, by the way. If that's not true, then I may need Luminus or something like it sooner than I thought.
Hello. Was looking for information on when to use a case
form and when to use multimethods (I want to dispatch on value), and also some performance comparisons. And I came across this https://insideclojure.org/2015/04/27/poly-perf/. When Alex says case
is "closed", does that mean anything more than "one has to add another case condition"? In contrast, multimethods are "open" because, you have define a multimethod external to the defmulti? Not quite able to understand why case is more closed than multimethods. If I need to make some changes to the value I'm dispatching on, I have to change the mulimethod definition as well isn't it?
if you are using a third party library where things are defined as case over defmulti in your code you are able to extend the defmulti, but not the case..
One major difference between case
and defmulti
is that defmulti
is more flexible. There's a couple of ways the difference in flexibility manifests. For case
, there's really only one way to add an additional clause which is to add another clause within the case statement. For defmulti
, there are multiple options for extending a multi method. For programs that are fairly static, the code for using either defmulti
or case
might look largely the same. but for a dynamic program, defmulti
's flexibilty can really shine.
Two types of extra flexibility come to mind.
1. Allowing library users to provide extensions. See https://clojuredocs.org/clojure.core/print-method for an example
2. Extending multi methods at run time. Typically, programs don't take advantage of this in production, but it's pretty useful in development if you're doing repl driven development. The idea is that you create a multi method that has 0 clauses and add/update clauses using your attached repl until everything works.
Thanks, the library bit makes sense. Need to try out the REPL friendliness of multimethods. š:skin-tone-4:
is there any name-spaced syntax for set similar like map, #{:a.b/foo :a.b/bar :a.b/baz} can i take this a.b out for once?
no, but even for maps you canāt take multiple values only by namespace
#:a.b {:foo 123 :bar 123}
in map i can do this,
and the key would all become :a.b/foo :a.b/bar, i guess there's no such thing for set then š
Does anyone have any idea what this error might be due to? I think it happens in a Luminus project where I am trying to set up Sente. The error happens when Sente tries to connect to the server. I would love some hints on how to debug it or tips if anyone knows what might be wrong.
java.lang.IllegalArgumentException: No implementation of method: :on-close of protocol: #'org.httpkit.server/Channel found for class: nil
at clojure.core$_cache_protocol_fn.invokeStatic(core_deftype.clj:583)
at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:575)
at org.httpkit.server$eval19781$fn__19851$G__19772__19858.invoke(server.clj:96)
at taoensso.sente.server_adapters.http_kit.HttpKitServerChanAdapter.ring_req__GT_server_ch_resp(http_kit.clj:25)
at taoensso.sente$make_channel_socket_server_BANG_$fn__21615.invoke(sente.cljc:606)
at muuntaja.middleware$wrap_params$fn__8119.invoke(middleware.clj:52)
at muuntaja.middleware$wrap_format$fn__8123.invoke(middleware.clj:73)
at everclear.middleware$wrap_formats$fn__9289.invoke(middleware.clj:41)
at ring.middleware.anti_forgery$wrap_anti_forgery$fn__6955.invoke(anti_forgery.clj:94)
at ring.middleware.params$wrap_params$fn__9021.invoke(params.clj:67)
at ring.middleware.keyword_params$wrap_keyword_params$fn__8807.invoke(keyword_params.clj:53)
at reitit.ring$ring_handler$fn__24478.invoke(ring.cljc:326)
at clojure.lang.AFn.applyToHelper(AFn.java:154)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.AFunction$1.doInvoke(AFunction.java:31)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.lang.Var.invoke(Var.java:384)
at ring.middleware.reload$wrap_reload$fn__4448.invoke(reload.clj:39)
at selmer.middleware$wrap_error_page$fn__4463.invoke(middleware.clj:18)
at prone.middleware$wrap_exceptions$fn__4704.invoke(middleware.clj:159)
at ring.middleware.flash$wrap_flash$fn__8160.invoke(flash.clj:39)
at ring.middleware.session$wrap_session$fn__8605.invoke(session.clj:108)
at ring.adapter.undertow.middleware.session$wrap_undertow_session$fn__8711.invoke(session.clj:88)
at ring.middleware.keyword_params$wrap_keyword_params$fn__8807.invoke(keyword_params.clj:53)
at ring.middleware.nested_params$wrap_nested_params$fn__8865.invoke(nested_params.clj:89)
at ring.middleware.multipart_params$wrap_multipart_params$fn__8997.invoke(multipart_params.clj:171)
at ring.middleware.params$wrap_params$fn__9021.invoke(params.clj:67)
at ring.middleware.cookies$wrap_cookies$fn__8484.invoke(cookies.clj:214)
at ring.middleware.absolute_redirects$wrap_absolute_redirects$fn__9209.invoke(absolute_redirects.clj:47)
at ring.middleware.resource$wrap_resource_prefer_resources$fn__9057.invoke(resource.clj:25)
at ring.middleware.content_type$wrap_content_type$fn__9157.invoke(content_type.clj:34)
at ring.middleware.default_charset$wrap_default_charset$fn__9181.invoke(default_charset.clj:31)
at ring.middleware.not_modified$wrap_not_modified$fn__9123.invoke(not_modified.clj:61)
at ring.middleware.x_headers$wrap_x_header$fn__8747.invoke(x_headers.clj:22)
at ring.middleware.x_headers$wrap_x_header$fn__8747.invoke(x_headers.clj:22)
at ring.middleware.x_headers$wrap_x_header$fn__8747.invoke(x_headers.clj:22)
at everclear.middleware$wrap_internal_error$fn__9283.invoke(middleware.clj:20)
at ring.adapter.undertow$undertow_handler$fn$reify__42283.handleRequest(undertow.clj:33)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:370)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2019)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1558)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1449)
at java.base/java.lang.Thread.run(Thread.java:831)
I get this error in the developer console by the way:
sente.cljc:1045 WebSocket connection to '<ws://localhost:3000/chsk?client-id=1aff9efa-b158-4247-84f8-4b21936a9085&csrf-token=zmd0tN%2BySX0N%2F58zu%2FFAg%2BpLKnac5jbJGxWK8kPbx6uTYxqjjimzsf4C9%2B%2FZE%2BwkC2%2F8ppHEHT9tX9XO>' failed:
eval @ sente.cljc:1045
core.cljs:159 ERROR [taoensso.sente:1058] - WebSocket error: [object Event]
eval @ core.cljs:159
These are the relevant lines mentioned in the stack trace (from middleware.clj
):
17 (defn wrap-internal-error [handler]
18 (fn [req]
19 (try
20 (handler req)
21 (catch Throwable t
22 (log/error t (.getMessage t))
23 (error-page {:status 500
24 :title "Something very bad has happened!"
25 :message "We've dispatched a team of highly trained gnomes to take care of the problem."})))))
26
27 (defn wrap-csrf [handler]
28 (wrap-anti-forgery
29 handler
30 {:error-response
31 (error-page
32 {:status 403
33 :title "Invalid anti-forgery token"})}))
34
35
36 (defn wrap-formats [handler]
37 (let [wrapped (-> handler wrap-params (wrap-format formats/instance))]
38 (fn [request]
39 ;; disable wrap-formats for websockets
40 ;; since they're not compatible with this middleware
41 ((if (:websocket? request) handler wrapped) request))))
re the original question, you had it. multimethod methods can be extended after the fact without changing the defmulti. case will require all cases to be defined at the same place.
no, maps only. the syntax would be pretty ugly for sets #:a.b#{:foo 123}
- that seemed bad :)
still sitting on the fence about vectors
is there anything as simple to use as slurp/spit for binary data?
FileOutputStream perhaps?
java.nio.file.Files
has readAllBytes
it's also available in this library: https://github.com/babashka/fs/blob/05d392933a4e6fe0e0c3fa002c1bdeeccc995801/src/babashka/fs.cljc#L527
and <http://clojure.java.io/copy|clojure.java.io/copy>
does support writing a byte array to a file
my use case would be reading a binary input stream (ring request body), checking how long the contents are and writing it into a file
this is really easy with text data with slurp and spit but binary data not so much
jio/copy might be the way to go hmm
you could just use the http://java.io inputstreams for this kind of thing
i know but i hoped there was something as simple as slurp/spit š
slurp/spit is easy, but not simple
Logging in java is such a frustration. I imported clj-http that messed with my current logging and logs stopped showing up on my cider. Is there a way to turn off the logging from clj-http?
@munichlinux What are you currently using for logging?
@seancorfield here is my setup:
[org.apache.logging.log4j/log4j-api "2.14.1"]
[org.apache.logging.log4j/log4j-core "2.14.1"]
[org.apache.logging.log4j/log4j-jcl "2.14.1"]
[org.apache.logging.log4j/log4j-jul "2.14.1"]
[org.apache.logging.log4j/log4j-1.2-api "2.14.1"]
here is my log4j2.xml:
<?xml version= "1.0" encoding= "UTF-8" ?>
<Configuration monitorInterval= "10" >
<Appenders>
<!-- console output -->
<Console name= "Console" target= "SYSTEM_OUT" >
<PatternLayout pattern= "[%level] - %d %logger %m%n%throwable" />
</Console>
</Appenders>
<Loggers>
<Logger name= "io.grpc.netty.shaded.io" level= "ERROR" >
<AppenderRef ref= "Console" />
</Logger>
<Logger name="org.apache.http" level="OFF" >
</Logger>
<Root level= "all" includeLocation= "false" >
<AppenderRef ref= "Console" level= "DEBUG" />
<AppenderRef ref= "Console" level= "ERROR" />
<AppenderRef ref= "Console" />
</Root>
</Loggers>
</Configuration>
And youāre using tools.logging
to do your actual logging, yes?
Read over this section: https://github.com/clojure/tools.logging/#selecting-a-logging-implementation
What I believe is happening is that previously, tools.logging
was picking up log4j2 by default but when you introduced clj-http
, it depends on commons logging which tools.logging
āprefersā, so you need to explicitly tell it to use log4j2 anyway (via the JVM option described in that link).
Hi, im having a bit of an issue calling a package private function from a base class. After doing a bunch of googling it seems others have had some problems too and was wondering if anyone had a solution?
@seancorfield yes using tools.logging.
@seancorfield I added that to :repl
section
:repl {:jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/log4j2-factory"]}
that did not do the magic either.
Are you sure that however you are starting up your Clojure process is using that? (profile/alias)
@kierand Can you provide a bit more context? That doesnāt sound like a Clojure problemā¦?
so i have the following code
(def pca
(-> (PublicClientApplication/builder client-id)
(.authority auth)
(.build)))
but .authority is inherited from an abstract class
so when i try to call it i get Reflection warning, clojure_ad_test/core.clj:22:7 - call to method authority on com.microsoft.aad.msal4j.PublicClientApplication$Builder can't be resolved (argument types: unknown).
That says it canāt figure out the type of auth
so it doesnāt know what method to call.
Youāll have to type hint auth
, either where you declare it, or inline.
Try (.authority ^String auth)
Based on these docs: https://javadoc.io/doc/com.microsoft.azure/msal4j/1.0.0/com/microsoft/aad/msal4j/PublicClientApplication.Builder.html
i tried that but i get the same errror
except type unknown is now java.lang.String
fyi those docs are wrong the actual signature of builder is public static class Builder extends com.microsoft.aad.msal4j.AbstractClientApplicationBase.Builder<PublicClientApplication.Builder>
Reflection warning, clojure_ad_test/core.clj:23:7 - call to method authority on com.microsoft.aad.msal4j.PublicClientApplication$Builder can't be resolved (argument types: java.lang.String).
Reflection warning, clojure_ad_test/core.clj:24:7 - reference to field build can't be resolved.
Execution error (IllegalArgumentException) at clojure.main/main (main.java:40).
No matching method authority found taking 1 args for class com.microsoft.aad.msal4j.PublicClientApplication$Builder
The only problem is on the cider repl. I do see the logs in nrepl-server
thats the entire stack trace
On Clojure 1.8, we get a different error:
(! 1452)-> clj -Sdeps '{:deps {com.microsoft.azure/msal4j {:mvn/version "1.0.0"}}}' -A:1.8
Clojure 1.8.0
user=> (import 'com.microsoft.aad.msal4j.PublicClientApplication)
com.microsoft.aad.msal4j.PublicClientApplication
user=> (let [b (PublicClientApplication/builder "foo")] (.authority b "bar"))
IllegalArgumentException Can't call public method of non-public class: public com.microsoft.aad.msal4j.ClientApplicationBase$Builder com.microsoft.aad.msal4j.ClientApplicationBase$Builder.authority(java.lang.String) throws java.net.MalformedURLException clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)
user=> ^D
(! 1453)-> clj -Sdeps '{:deps {com.microsoft.azure/msal4j {:mvn/version "1.0.0"}}}' -A:1.10
Clojure 1.10.3
user=> (import 'com.microsoft.aad.msal4j.PublicClientApplication)
com.microsoft.aad.msal4j.PublicClientApplication
user=> (let [b (PublicClientApplication/builder "foo")] (.authority b "bar"))
Execution error (IllegalArgumentException) at user/eval138 (REPL:1).
No matching method authority found taking 1 args for class com.microsoft.aad.msal4j.PublicClientApplication$Builder
user=>
I wonder if @ghadi can shed some light on this?(ISTR this is an area where Clojure has made some changes over the years but Iām cloudy on what they were)
can someone link to the javadocs for this target class?
Linked above.
Then ask in #cider ā I havenāt used that for years so I canāt help you, sorry.
The is an ask.clojure for this issue
There is
I thought it sounded a bit familiar. Iāve upvoted that ask issue.
This is an old problem (https://clojure.atlassian.net/browse/CLJ-1243 ) that really links back to an even older still open issue in Java https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4283544
Yech, what a mess
yeah i went through all of those issues and they all seem to end at a dead end
if you are just looking for a work around to make it work something like
(import 'com.microsoft.aad.msal4j.PublicClientApplication)
(def authority
(.findVirtual
(java.lang.invoke.MethodHandles/lookup)
com.microsoft.aad.msal4j.PublicClientApplication$Builder
"authority"
(java.lang.invoke.MethodType/methodType
com.microsoft.aad.msal4j.ClientApplicationBase$Builder
[String])))
(let [b (PublicClientApplication/builder "foo")]
(.invokeWithArguments authority [b "foo"]))
should workyep that works i had to make small change from com.microsoft.aad.msal4j.ClientApplicationBase$Builder
to com.microsoft.aad.msal4j.AbstractClientApplicationBase$Builder
but i can call the method.
Thanks so much!!
wow. this one was kinda sounding impossible to get the Clojure compiler to output the correct bytecode
thereās a method selection bug
will try to understand the behavior and documentat
so just out of curiousity - is this something that clojure can fix or something that has to be fixed at a java level?
it is kind of complicated
there are kind of two ways method invoking like (.authority b "foo")
can get compiled
one way is without reflection, which the compiler does if it knows all the types, but there appears to be a clojure bug somewhere so it is failing to do that
if a method call cannot be compiled without reflection, the compiler generates code that will do a reflective call at runtime, which would hit the underlying java issue
Ah so it's trying reflection because it should know the types but then the bug stops it from getting the abstract class it inherits from?
Would it be possible to point a function at a directory on my computer, have it find all of the image files, and then create a map according to this schema?