This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-03-06
Channels
- # babashka (60)
- # beginners (36)
- # clj-kondo (29)
- # clojure (91)
- # clojure-dev (18)
- # clojure-europe (12)
- # clojure-nl (1)
- # clojure-norway (11)
- # clojure-uk (5)
- # clojuredesign-podcast (8)
- # clojurescript (40)
- # core-typed (74)
- # data-science (8)
- # datomic (9)
- # emacs (22)
- # events (5)
- # fulcro (56)
- # gratitude (3)
- # hyperfiddle (11)
- # lsp (6)
- # malli (36)
- # meander (23)
- # off-topic (50)
- # polylith (4)
- # portal (10)
- # reitit (4)
- # schema (1)
- # shadow-cljs (66)
- # squint (3)
- # tools-deps (16)
"Web frameworks" makes it sound as if you're looking for something fullstack. Not that many fullstack CLJS solutions. If you're just looking at the frontend, then still Reagent or one of the React wrappers. Wouldn't call them frameworks though - just libraries.
:man-shrugging: People have different opinions on what to call a framework or a library. React calls itself a library. Any wrapper that doesn't add too much would then also be a library. Re-frame is probably a framework, and calls itself as such, but it's just Reagent + storage and it's very flexible, doesn't give off common framework vibes.
It's not CLJS, it's both CLJ and CLJS. It also uses HTMX on the frontend, which definitely can't be called mature.
shadow-css isn't "mature" but for what i do it's feature complete and works well enough
I myself use shadow-cljs and re-frame. But the first is a tool and I treat the second one as a library. :) A paramount, in my case, but still a library.
I don't think calling re-frame a framework would be too much of a stretch. I recall a discussion from the other day where someone said that you can switch libraries with relative ease but not so much frameworks. In that sense re-frame is just so all-encompassing that it'd be really tough to replace
I heard once, think it was on Clojure-related podcast, “You call a library, a framework calls you.” I know, that’s tangential to the original question. I use shadow-cljs as a build tool, like @U2FRKM4TW, and reagent and re-frame. My CSS is lovingly hand-coded.
I don't think frameworks are inherently bad. I have had way too much horrible experiences with a "mature" framework called Ruby on Rails, but then re-frame is a joyride where my biggest issue ever is how I organize stuff
I've been using Hono from Clojurescript lately and am enjoying that experience quite a bit!
I'm working on a library that wants to abstract over many data sources (databases, APIs, etc.) using a generic CRUD API and basic query data structures. that part is fine!
but an important point is that it should work on both CLJ and CLJS, and that some data sources are async. since blocking is not an option in (CL)JS, I had picked up Promesa as an ergonomic way to handle async things that's cross-platform.
it's working, but the pain point I'm having is around writing tests for async things in a CLJC-friendly way. blocking is out of the question, and cljs.test
has its async
macro that has no counterpart in CLJ. I've tried to use https://github.com/rwstauner/cljc-test-async but I'm still having difficulties.
I'm fine with digging in and trying to wrap or improve Promesa or cljs-test-async. but am I missing a whole different approach that would be better?
if I am to continue on this path, my best guess at a path to success here is that Promesa's own tests set an alternative executor in a fixture, for CLJ tests. I think that would solve the issues I'm having with tests running off-thread, and their results not getting reported properly.
if that does solve my issues then I think the executor thing should be a promesa.test
helper upstream, plus updating cljs-test-async
with some new helpers to make it easier to use.
I mean creating an async
macro that also works in CLJ should be simple as you have the option to block?
sure, that part is straightforward.
the main pain point seems to be CLJ tests running off-thread, which I understand is how the default executor for promesa in CLJ works. since they're running off thread, they're missing some bindings like *test-out*
and the clojure.test/report
functionality.
I was looking at the https://github.com/funcool/promesa/blob/master/test/promesa/tests/core_test.cljc#L87 for guidance, but they have separate CLJ and CLJS code, and in CLJ the test expectations are running on-thread after the async bit is done.
clojure.test doesn't use any threads at all, but it does use bindings, which in CLJ are thread local
so if a fn is called in another thread it won't have those bindings, unless using bound-fn
ah, sorry, I think I phrased that poorly.
what I meant to say is: clojure.test
expects the tests to all run in the same thread, since it relies on several (thread-local) bindings like clojure.test/*test-out*
. the pain point is that the default executor for promesa in CLJ runs promise-y things in a separate thread, so they're invisible to the test.
but yes, clojure.test expects the tests to run in single thread, or I guess with bound-fn
that doesn't really matter as long as it finishes within the bounds of the test
😢 it's really unfortunate to land there when the API I'm testing is identical between the platforms. but yes, async testing is a disaster. async anything is a disaster and JS not supporting blocking (or even pretending to) is a nightmare. async/await is an improvement but it's still "contagious".
for CLJ(S) async testing I've used this strategy: https://github.com/lilactown/flex-old/blob/157f8e36eb295902147c757c1712693daf1a6ab5/packages/flex-core/test/flex/test_macros.cljc
basically an async-test
macro that in CLJ is just a do
and in CLJS it's a (async done (go ~@body (done))
(or a p/do
for promesa could be done)
Interesting... That looks plausible. Do you think core.async would be a better pick for a library like this? I've always found it kind of obtuse, but maybe it's finally time to learn it properly.
I'd probably pick promesa today for a lib since it interops a lot better with native promises
I ended up doing something similar to @U4YGF4NGM’s suggestion to make this work:
• I defined an async
macro that's transparent in CLJ and wraps with (cljs.test/async done# (p/finally (p/do ~@body) (fn [_#] (done#))))
on CLJS.
• and added a clojure.test/assert-expr
/`cljs.test/assert-expr` for p=
that unwraps the values properly.
◦ in CLJ it wraps the two values with checks for being a promise, and deref
s them if so; that blocks and it's a vanilla =
.
◦ in CLJS it rewrites them into (p/let [exp# ~exp, act# ~act] (test/is (= exp# act#)))
.
• that carries a restriction that the (is (p= ...))
need to be top-level , only nested in testing
. anything more would break the promise wrapping in (p/do ...)
it's tempting to rebuild this so that my async
macro does something clever like postwalk
ing the entire form looking for arbitrarily-deep p=
calls and hoisting their values into a top-level p/let
. I think that's practical, but it's more tricky and might break things.
the above restriction is not too painful in my use-case, and it's much less painful than writing almost-identical tests in CLJ and CLJS.