I have a "debugging cljfx" question. Because I'm still new at this, I'll frequently get errors from deep inside cljfx rather than within my own code. What do I mean by that? Well obviously, it's my code that caused the error. I haven't actually found a bug in cljfx. But what I mean is that the stack trace that gets dumped out doesn't have a line reference for my own code in it anywhere. Here's an example.
Recent stack trace
Obviously I've passed a nil somewhere to the renderer and the renderer doesn't know how to render a nil, so it throws an error. But how can I know where I passed the nil?
I made cljfx/dev to help with that: https://github.com/cljfx/dev?tab=readme-ov-file#improved-error-messages-with-spec
😆 I love that you think I'm smart enough to fix this, lol Serious talk though, I'll give it a look this weekend.
@vlaaad Sorry to ping this, but I really want to understand what's going on here.
I didn't have time to look into it yet
I had a quick look, and the problem seems to be that with the context is nil when it's accessed
So it seems that using dev lifecycle prevents context from being passed further
It's probably a bug in dev lib
PR with a fix would be welcome 🤗
I figured it out! It was not a bug in the code, there it could be seen as a documentation bug.
The correct incantation for the renderer is:
clojure
(def dev-renderer
(fx/create-renderer
:middleware (comp
fx/wrap-context-desc
(fx/wrap-map-desc (fn [_] {:fx/type views/root})))
:error-handler (bound-fn [^Throwable ex]
(.printStackTrace ^Throwable ex *err*))
:opts {:fx.opt/map-event-handler event-handler
:fx.opt/type->lifecycle (dev/wrap-type->lifecycle
{:type->lifecycle #(or (fx/keyword->lifecycle %)
(fx/fn->lifecycle-with-context %))})}))
Note that I'm passing a map to dev/wrap-type->lifecycle. It's not immediately clear that this is required from the documentation.I'll submit a pull request that adds this example code and an explanation of it later this week
ohh, right, I forgot it takes kv-args as argument!
It's all good. I figured it out now, which will be really helpful as I work out how to move this into a client-server model.
I've legitimately got no clue where to start with that
So given that http://cljfx.dev is only a requirement when you're in... development mode, would you add http://cljfx.dev to your require in the core.clj file and have it always present, even though it isn't used? Or do you do something else?
I’d use requiring-resolve in an if branch that checks that we are in the dev mode
there is example that does requiring-resolve there
Aha, ok. Now I'm tracking
you shouldn’t always require it!
Yeah, that's what I was thinking
Here's what I'm building, btw. It's... still pretty ugly, I haven't gotten to adding cljfx/css yet. But it's coming along! https://github.com/jonathanabennett/megastrike/
It does somewhere between 50 and 70% of the "standard rules" for the board game it's emulating at this point (depending on how you count the unit special abilities).
So something like this?
(def dev-renderer
(fx/create-renderer
:middleware (comp
fx/wrap-context-desc
(fx/wrap-map-desc (fn [_] {:fx/type views/root})))
:error-handler (bound-fn [^Throwable ex]
(.printStackTrace ^Throwable ex *err*))
:opts {:fx.opt/map-event-handler event-handler
:fx.opt/type->lifecycle (apply @(requiring-resolve 'cljfx.dev/wrap-type->lifecycle)
{:type->lifecycle #(or (fx/keyword->lifecycle %)
(fx/fn->lifecycle-with-context %))})}))hmm, do you need 2 renderers though?
I’d use a single one, e.g.
(def type->lifecycle #(or (fx/keyword->lifecycle %)
(fx/fn->lifecycle-with-context %)))
(def renderer
(fx/create-renderer
:middleware (comp
fx/wrap-context-desc
(fx/wrap-map-desc (fn [_] {:fx/type views/root})))
:error-handler (bound-fn [^Throwable ex]
(.printStackTrace ^Throwable ex *err*))
:opts {:fx.opt/map-event-handler event-handler
:fx.opt/type->lifecycle (if in-development
(@(requiring-resolve 'cljfx.dev/wrap-type->lifecycle) {:type->lifecycle type->lifecycle})
type->lifecycle)}))It works! Awesome, thank you
@vlaaad So I am trying to figure out how to use cljfx/dev for that and I cannot figure out what's wrong. Here's where I've gotten to with my https://github.com/jonathanabennett/megastrike/blob/dev/src/megastrike/core.clj file that launches the program. When I launch it with in-development? set to false, it works (but I have a lifecycle nil error I need to track down). When I launch it with in-development? set to true, it fails to open a window at all and it gives me the following error:
clojure.lang.ExceptionInfo: Cannot invoke "java.util.concurrent.Future.get()" because "fut" is null
The only lines in my program which are in the stack trace are a sub function that subscribes to the current display value to know which screen to show, which is pre-populated in the state atom correctly.please share the full stacktrace
Here it is. Sorry, my evening was running a robotics tournament, couldn't post sooner.