This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-06-02
Channels
- # ai (1)
- # aleph (16)
- # announcements (1)
- # architecture (51)
- # babashka (32)
- # beginners (27)
- # calva (3)
- # clerk (1)
- # clojure (49)
- # clojure-art (1)
- # clojure-denver (6)
- # clojure-europe (70)
- # clojure-nl (1)
- # clojure-norway (56)
- # clojure-uk (2)
- # clojuredesign-podcast (4)
- # clojurescript (57)
- # clr (15)
- # community-development (3)
- # conjure (1)
- # core-async (10)
- # data-science (1)
- # datalog (2)
- # datomic (3)
- # emacs (12)
- # events (1)
- # gratitude (4)
- # honeysql (9)
- # hyperfiddle (86)
- # jobs (4)
- # off-topic (10)
- # pedestal (5)
- # portal (11)
- # practicalli (2)
- # reitit (7)
- # releases (3)
- # remote-jobs (1)
- # sql (15)
- # tools-build (8)
- # xtdb (4)
What would be the primitive to go from electric-flow to discrete events (for consumption outside electric)? From previous posts I've understood you need m/observe to get external-events into electric-land, so what's the opposite of that?
#?(:clj (def !notification
(atom
{:id 0
:message "Notification"})))
(e/defn Notifications []
(e/client
(let [notification (e/server (e/watch !notification))]
(dom/div
(dom/props {:class "demo-element"})
(dom/div
(dom/h3 (dom/text "Notifications"))
(ui/button
(e/fn []
(e/server (swap! !notification update :id inc)))
(dom/text "Simulate server push"))))
(when notification
(show-notification notification)))))
I would say it's a work-around because I need to add/update the id else the when-form wont run, but I really don't need the id otherwise.
It's fine for now but I ran into this confusion before, that when
only runs on differing values, it's fine/great aslong as you're working within electric, but it get's confusing/unwanted when you try to interop with external system (a simple cljs call in this case)
Your question is, how do you send a discrete event from client to server? You just did, swap!
is a discrete side effect, instead of swap! you can write send-email
if you want
If the question is: how do you stream discrete events from server to client and directly into the DOM (without reducing them into a server side atom first)? Specifically how do you avoid the work-skipping on duplicate value which is inherent to continuous time signals that Electric uses? We have a new primitive coming soon for rendering discrete streams, for now just use an atom, the when
will not work skip here if the id
changes (even if the :message
is the same twice in a row)
Iām trying to use Calva to jack in the xtdb starter code. I select the ādeps.edn + shodowjsā, however it keep waiting to connect the shadow cljs. Anyone has seen this happened?
don't choose shadow-cljs
I actually run the app via the command line, and connect from vscode via nrepl
using "Generic"
and the port printed from the command clj -A:dev -X user/main
clj -A:dev -X user/main
Starting Electric compiler and server...
shadow-cljs - server version: 2.20.1 running at
shadow-cljs - nREPL server started on port 9001
[:dev] Configuring build.
[:dev] Compiling ...
[:dev] Build completed. (221 files, 220 compiled, 0 warnings, 6.84s)
š App server available at
here, 9001
I dont actually know if this is a perfect REPL, I am still learning VSCode
The traditional jack-in command "Start a project repl and Connect (jack-in)" with deps.edn (Not shadow) also works in calva but it intermittently fails on my machine in a way I haven't tried to debug yet because it only fails when run from Calva, which means its not an Electric issue
if you want to have both REPLs (`deps.edn` and shadow-cljs
) simultaneously connected to your VS code editor, you would need to open the project in two separate windows. Now, VS code does not really allow it to open the same folder twice (it reuses the already open window) but you can trick it by opening one as a folder and the other as vscode workspace (you can create one when you open a folder and choose from menu file > save as workspace...
).
Then you can jack-in to deps.edn
and shadow-cljs
in each window separately, deps.edn + shadow-cljs
option doesn't work.
That's the way I have been using VScode for fullstack clojure projects, not only electric ones. Electric adds this additional constraint of shadow-cljs needed to be run from the same JVM as the server, so in that second window you might need to use connect to running repl
instead of jack-in.
@UHA0AQZ2M have you personally used this configuration with Electric?
thanks
I've been running deps.edn + shadow-cljs in vscode all the time in my project. Once it compiles and waits for cljs I manually load and start the electric server from a user.clj file and then open a browser tab to localhost:8080, which finishes connecting cljs
calva can switch between clj and cljs repls in same window without issue usually
alan doesnāt that configuration cause you to have two JVMs?
I'm still learning a lot of things so it's possible I'm doing something flagrantly wrong š but it's been working without any obvious issues. Is there anything obvious that would tip off creating two jvm's?
in electric specifically your client and server code versions will get out of sync, youāll start to notice it with deeper require hierarchies
the symptom is typically hot code reloading glitches
@U051Q05GSG7 ās setup will only create one JVM. Or, at least the two REPLs will be spawned from the same JVM. I describe whatās going on a bit here: https://blog.agical.se/en/posts/shadow-cljs-clojure-cljurescript-calva-nrepl-basics/
@U0ETXRFEW where can I read the source code of what is happening when "deps.edn + shadow-cljs" is selected
I've mostly been working on clientside code, but anywhere I have client/server mixed hot reload has been fine. Couldn't speak yet to more fleshed out programs
I recommend using the shadow-cljs project type when connecting Calva. Hereās a PR on that starter project adding some VS Code config that makes starting the app and connecting Calva a matter of pressing ctrl+alt+c ctrl+alt+j
. https://github.com/hyperfiddle/electric-xtdb-starter/pull/2
@U09K620SG Connecting the REPL starts here in Calva: https://github.com/BetterThanTomorrow/calva/blob/61c45bf8a1f748171400c33de8a0858d01cf85da/src/connector.ts#L66 Then on line 151 the ClojureScript REPL is considered. The code for starting a shadow-cljs ClojureScript REPL comes from the configuration here: https://github.com/BetterThanTomorrow/calva/blob/61c45bf8a1f748171400c33de8a0858d01cf85da/src/nrepl/connectSequence.ts#L265 I think the most relevant part there is:
startCode:
"(do (require 'shadow.cljs.devtools.server) (shadow.cljs.devtools.server/start!) (require 'shadow.cljs.devtools.api) (shadow.cljs.devtools.api/watch %BUILDS%))"
thank you @U0ETXRFEW
is there a template setup for datascript (on the server) somewhere? I've tried xtdb but am not convinced that their record model works for me... they say you shouldn't put nested things as values since they won't be indexed, but when putting stuff into the db that means having to manually disassemble everything into separate ::xt/put operations and id references. Also the bitemporal stuff is neat but I don't really need it
datomic would also be fine but I remember the setup and licensing to be quite complicated.
It's probably transitive from Electric repo, which the most recent published version depends on datascript. (We don't depend on it in Electric repo anymore so we'll need to fix the starter app)
Datomic Pro (which is now free) is easy to set up, and DataScript does not include persistence
Does anyone have a more complete auth example based on extended chat example with various username/password flows? As far as I can tell, the main thing I need to do is call res/set-cookie
only on condition of correct credentials and then use username (or user ID) to look up user-specific data?
Hyperfiddle-2020 has an Auth0 integration which we'll be porting to Electric in the next couple months - that will unlock anything Auth0 supports. (Assuming we choose to stay with this provider)
We will land it in a sample app for testing
In the electric-starter-app, wrap-demo-authentication
calls (next-handler ring-req)
before (res/set-cookie res "username" ā¦)
. Shouldnāt calling the next handler be deferred until after credentials are authenticated? I know itās only a demo middleware, but I imagine most users will check auth around the set-cookie
call.
https://github.com/hyperfiddle/electric-starter-app/blob/cd8e92b8aa0de51f02d59036cfc02fc6dc9df650/src/electric_server_java8_jetty9.clj#L25-L27
The demo works correctly - https://electric.hyperfiddle.net/user.demo-chat-extended!ChatExtended
are you saying there is an issue in the demo?
I think that line is the /auth endpoint which is unauthenticated on purpose (allowing the user a chance to see the prompt and login). You would check the password before setting the cookie
the app endpoint doesn't actually check auth in the middleware, it simply binds the request and passes it to Electric so your Electric app can perform the auth check as in the demo. That is important so Electric can render the login page (which in this case is a link to somewhere else since the demo uses basic auth, but it could be anything)
Just FYI, the xtdb integration in electric-xtdb-starter has a minor issue. https://github.com/hyperfiddle/electric-xtdb-starter/blob/master/src/app/xtdb_contrib.clj#L14 :xt/tx-time
should be ::xt/tx-time
or :xt.api/tx-time
I suspect a colon was dropped at some point, but if you print the value the key is :xt.api/tx-time
I'm not sure if that repo is still being used but I'm mentioning it here in case others have the bug in their code like I did
thanks iāll get it fixed
if you send a PR by tomorrow iāll get you the contributor badge if you want it
how can i ensure certain dom elements wait on certain variables to be there? using the when
is helpful but now it just sometimes doesn't show the image
for example, I Have this as the functive part of the albumPage
(e/defn AlbumPage
"Album page for specific album by id"
[incoming-id xt-id]
(e/server
(let [e (xt/entity db xt-id)
album-id (:album/id e)
title (:album/title e)
last-modified (:album/last-modified e)
created-at (:album/created-at e)
modified-by (:album/modified-by e)
art (:album/art e)]
(e/client
(.log js/console art
(dom/div (dom/props {:class "album-page"})
(when art
(dom/img (dom/props {:src (str "img/" art) :class "albumart" :title art-description})))))))))
youāre saying that if the art reactive value becomes available after the dom/img first mounts then the img src prop doesnāt properly load?
i donāt think i understand, can you send me a repo to clone with the issue?
like why would I get null
for the image value, it is loading before the reactive value has it I guess? maybe I am reading from the wrong spot
it's just inconsistent based on where i navigate from which is weird. like i have 2 separate views with buttons that lead to the same "page"
i think it might just be my database is fuzzy, now that i inspect IDs. like i might have duplicate entries in the DB -_-
Is that a typo in the copy/paste or is the call to dom/div
inside the js/console.log
call?
itās allowed because the dom helpers maintain the dom by side effect (point write)
@U04PBP0MYS3 it could be a HTML spec issue where the img tag only looks at the initial value of the src attribute
however (when art ā¦) should be doing exactly that iiuc
When art becomes nil, does the value of the dom/div
change, i.e. would it cause the console.log to run again (and log nil
for art)?
the console.log will be work-skipped the second time because the argument to it will be nil twice in a row, so the second call will be skipped, essentially parking the reaction until something changes
as to why it returns nil - the dom macros take a ācontinuationā & body and return the result of evaluating the continuation. so, (dom/div (inc 1)) will return 2 and the inc will evaluate after the div is mounted, this is so that in (div (span)) the span evaluates in the context of the div. the span is the continuation of the div. it is much more intuitive to use than to explain, normally we donāt think about dom evaluation order we just expect it to nest and this is the consequence
another pattern is (let [$el (dom/div dom/node)]ā¦) wherein we capture the div element reference after it is created and return it via the continuation
yea i think i'm just a goof ball and am querying via xtdb for tracks when i meant album x)
i started deleting items to get to the bottom of it, and important things started vanishing lol
Btw. @U055PQH9R4M this is purely a convenience, but instead of destructuring each attribute like so: (let [x (:album/x e), y (:album/y e) ā¦] ā¦)
, you can do:
(let [{:album/keys [id title last-modified created-at modified-by art] e]
(when art ā¦)
@U051SPP9Z that's really nice, i will have to learn this pattern
@U055PQH9R4M https://clojure.org/guides/destructuring#_associative_destructuring