@tony.kay I’m loving using Guardrails, and I know precisely the feeling that triggers me bringing it into a project — when I find docstrings or function arguments that no longer make sense to me, weeks or months after I wrote that code. 🙂 Thanks for your great work here!
I’m looking for help on something happened to me today — it’s actually something that happened using Ghostwheel, too.
I added Guardrails to the project, and started using the >defn
forms with no problems (and Guardrails forms were recognized by Cursive)I but then later found that arguments weren’t being checked.
Is there a checklist to confirm that Guardrails is working properly?
I have :jvm-opts ["-Dguardrails.enabled"]
in my deps.edn
I ran this form, which was accepted:
(>defn tt
[x] [int? => string?]
(+ 1 1))
But this form ran, without an error:
(tt "abc")
=> 2
(I expected passing in a string to result in an error…). Any tips? Thank you, all!I don't really know but I believe that the default behavior in Q might be to only log warnings and you must switch on throwing exceptions.
Check your (System/getProperty "guardrails.enabled")
and make sure it is actually set in your REPL
There’s very little to the macro, other than the config + turning it on/off
Hmm!! Okay, that was super helpful!! Obviously, I don’t understand :jvm-opts as well as I thought! 🙂
> (System/getProperty “guardrails.enabled”)
> => nil
Huh. I just spent 30m scouring the docs on deps.edn…. Is it not allowed to put a :jvm-opts
at the highest level in a deps.edn
file?
{:src ["src"]
:jvm-opts ["-Dguardrails.enabled=true"]
:deps {analytij/analytij {:mvn/version "0.4.0"}
com.fulcrologic/guardrails {:mvn/version "0.0.12"}
org.clojure/data.csv {:mvn/version "0.1.4"}
vlaaad/reveal {:mvn/version "1.1.164"}}
:aliases {:test {:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "1.0.700"}
org.clojure/test.check {:mvn/version "1.1.0"}}}}
THANK YOU!…holy cow… :jvm-opts
must be inside of an alias.. 😱
https://clojure.org/reference/deps_and_cli
Prepare JVM environment
JVM arguments may either be passed on the command line (with `-J`) or by using data stored in an alias under `:jvm-opts` and passed with `-X` or `-A` or `-M`
It’s working. Thank goodness. (Trying not to weep at the time I spent on this…. Worth it, though! 🙂
@tony.kay BTW, can one put things like #(instance? LocalDateTime %)
(or String or any class) into the parameter types? When I try, I get this error.
Thx for your great work here, and I’m looking forward to your commercial offering — consider me on the “must buy” list, as well as happy to test anything you have!
(>defn tt2
[x] [int? => #(instance? java.time.LocalTime %)]
(+ 1 1)))
Syntax error compiling at (src/analytics/guardrails_test.clj:10:1).
Unable to resolve symbol: fn* in this context
yep…parsing limitation. Might fix eventually, but for now just s/def
that and use the keyword instead
Thank you! Makes total sense now! And PS: just watched your RAD video on YouTube, after listening to your interviews on ClojureScript podcast and scanning all the docs. Wow. I'm inspired to give it a try this weekend!
Thanks for all your great work — Fulcro RAD seems like the closest we've come to Microsoft Access for designing form based apps. That's meant as a huge compliment!!! Keep up all your amazing work!
(And there is an empty {}
guardrails.edn file in my project directory…)
(I’m using Clojure Version: 1.10.1.727, java/11.0.5-fx-zulu)
I'm having trouble getting a delete mutation to work when I enable a remote on the mutation. I'm trying to delete [:foo/id 1]
using the following code:
(defsc Foo [this {:foo/keys [id text] :as props}]
{:query [:foo/id :foo/text]
:ident :foo/id}
(div
{:onClick #(comp/transact! this [(m/delete-foo)])}
"foo: " id " text: " text))
(def ui-foo (comp/factory Foo {:keyfn :foo/id}))
(defsc Root [this {:keys [foo]}]
{:query [{:foo (comp/get-query Foo)}]
:initial-state {}}
(div
(div "foo stuff")
(when foo
(div (ui-foo foo)))))
(defmutation delete-foo
[_]
(action [{:keys [state]}]
(swap! state update :foo/id dissoc 1))
(remote [env] true))
;; resolvers.clj
(def foo-table
(atom
{1 {:foo/id 1 :foo/text "testing foo"}}))
(pc/defresolver foos-resolver [env input]
{::pc/output [{:foo [:foo/id :foo/text]}]}
{:foo (get @foo-table 1)})
;; mutations.clj
(pc/defmutation delete-foo [env input]
{::pc/sym `delete-foo}
(log/info "Deleting :foo/id 1 from foo-table")
(swap! foo-table dissoc 1))
(defn ^:export init []
(app/mount! app ui/Root "app")
(df/load! app :foo ui/Foo)
(js/console.log "Loaded"))
With the remote enabled, the resulting app state looks like this (instead of :foo/id {}
which is what I expect):
:foo/id {1 nil}
And after doing some searching, it looks like the issue is being caused by this:
https://github.com/fulcrologic/fulcro/blob/4a9f28f9099bfae7028a28d42fa9a8a3f1d33aa9/src/main/com/fulcrologic/fulcro/mutations.cljc#L98
After the client mutation deletes [:foo/id 1]
, at root I have :foo/id {}
. So far so good. But... when the
update-errors-on-ui-component!
function runs, it finds no remote-error?
, and uses update-in s ref dissoc k
, resulting in the following:
core.cljs:198 #p ref => [:foo/id 1]
core.cljs:198 #p (:foo/id s) => {}
core.cljs:198 #p (remote-error? (:result env)) => false
core.cljs:198 #p k => :com.fulcrologic.fulcro.mutations/mutation-error
core.cljs:198 #p (:foo/id (clojure.core/deref state)) => {1 nil}
Is this a bug or have I missed some portion of the documentation that explains it? Maybe some of my assumptions are wrong? I'm new to Fulcro so I must assume it's not a bug - it's an old function. I just can't understand how to fix it -- or if it needs fixing at all 🤕1. You can choose to delete from a parent. Then the “component” won’t be the one you’re deleting. 2. You can change the default result action. It is pluggable. Take out the errors on ui component stuff, perhaps you don’t even use it. 3. You can choose not to GC this particular thing…there’s a lot of RAM in a browser
One thing that strikes me is your global resolver is liable to return {:foo nil}
. You could slightly alter things so that if (get @foo-table 1)
is nil
then your resolver actually returns say :unknown-foo
. I regard any resolver returning nil
as a bug in my own code.
That's a good rule of thumb to remember, no resolver should ever return nil
. Thank you guys. It ended up being a silly fault of my own but your replies were helpful nonetheless.
Buggy input range behavior I cannot quite reproduce, hence seeking opinion.
It seems that modifying both the max
value and the value
itself at the same time when rendering prevents the value
from being updated in the DOM. Has this happened to you before?
I am logging both props at rendering and in componentDidUpdate
, I am absolutely sure that both are correct and inspecting the DOM shows the max
value changing as expected but not the value
.
After a few hours of hair pulling, my simple hack is to keep max
at 1 and deal with value
as a percentage of the real value in my data, but the core problem is very troubling.
are both in the query? All components come with a shouldCopmonentUpdate that checks for props diffs. If props don’t change, components don’t re-render…and if things are not in the actual query, then targeted refresh will not send them (and thus nothing will change). If rendering is broken, this is almost always the cause.
They are, I even println
them in the rendering function to make sure that all values used for rendering are indeed what they should be. Still, when a mutation leads to changing both max
and value
at the same time, only max
gets updated in the DOM although I know for a fact value
changed as well (ie. its prop). Furthermore, If I save my files and trigger compiler + re-rendering, then value
gets updated as expected.
Well, to be more precise, I noticed some float wizardry. value
doesn't stay exactly the same, it's changing from 1.444446 to 1.4444457 for instance. No idea what is happening there! Disclaimer, that component is not exactly trivial, it uses getDerivedStateFromProps
for instance, there is room for a weirdness. However I did spend some time on it, trying different variations, the end result was always the same.
I was wondering if it wasn't due to React, maybe something like the algorithm updating max
before value
? But I would have a hard time believing no one noticed this before.
If you get to a small reproducible issue that I can pull into a workspace card it would be helpful. I don’t have the time to do much else. I’m not aware of any Fulcro issues, but I’m sure getDerivedStateFromProps
is not heavily used.
@tony.kay All right, the problem kept reappearing... and the solution is weird. I have no idea why but working with float values (even set as strings, as they should) results in the DOM input range updating its value only every couple of times, and this happens only with renders happening after mutations, not after set-state!
. The rendering function runs all right, everything is fine, all props are correct, it really is about the DOM (React itself?) and the value
(other DOM properties such as max
were always updating as expected).
The issue completely disappeared since I've starting working exclusively with integers. Spooky. If you hear about this again someday I recommend mentioning it in the book since it is a particularly strong head-scratcher (hours spent on the matter...)
Is there something special you have to do to get child components to render from an imported react component library? I’ve been banging my head against a wall for ages now and not sure what I’m doing wrong. This is what I’ve got at the moment and not sure why only the app container component is being render? Very new to clojure and fulcro and not sure whats wrong.
(def ui-app-container (interop/react-factory libComp/AppContainer))
(def ui-header (interop/react-factory libComp/Header))
(defsc Root [this props]
(ui-app-container (ui-header)))