This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-04-22
Channels
- # adventofcode (12)
- # announcements (17)
- # babashka (46)
- # beginners (105)
- # calva (7)
- # chlorine-clover (7)
- # cider (9)
- # clj-kondo (41)
- # cljsrn (16)
- # clojure (67)
- # clojure-australia (1)
- # clojure-europe (24)
- # clojure-france (6)
- # clojure-germany (10)
- # clojure-italy (1)
- # clojure-losangeles (3)
- # clojure-nl (4)
- # clojure-uk (11)
- # clojurescript (8)
- # cursive (8)
- # data-oriented-programming (1)
- # data-science (1)
- # datomic (11)
- # defnpodcast (4)
- # events (1)
- # fulcro (34)
- # graalvm (6)
- # helix (3)
- # jackdaw (19)
- # jobs-discuss (1)
- # leiningen (9)
- # luminus (2)
- # malli (15)
- # mathematics (2)
- # meander (5)
- # mental-health (1)
- # off-topic (4)
- # pathom (23)
- # podcasts (1)
- # polylith (4)
- # quil (3)
- # re-frame (81)
- # react (1)
- # reagent (19)
- # reitit (4)
- # releases (1)
- # reveal (11)
- # shadow-cljs (54)
- # specter (14)
- # tools-deps (16)
- # vscode (2)
- # xtdb (3)
I see function names like put! or <! etc. What does the "!" exclamation mark mean? And when should I name my own functions with this character appended?
core.async
has its own peculiarities around those functions too — the single !
means non-blocking (must be used inside a go
block/loop) and the double !!
means blocking.
in a macro, i have a let block where i put some expressions in a map. within the returned quasi-quote expression, i want to fetch one of those expressions from the map without resolving it, and then resolve it later. is that possible?
Something to keep in mind is macros usually generate code that does things, they don't do things
let me think on how best to reword this, then. the solution i've found tonight is to wrap the expressions in a function, and then call that function when i need it, but idk if that's the best method
I realise this depends a lot on my setup, but how do I deploy a Reagent app to Heroku?
I tried serving the index.html
file of my app via ring-jetty
, but it seems it’s not that simple
Is there a server side piece or is the app all client side?
I can already handle HTTP requests just fine and even serve handcrafted HTML files, but it’s just blank otherwise
So you have it working locally on your computer, but it doesn't work when you deploy?
@U7RJTCH6J Ah, well there should be a server-side piece because of the database I’ll connect in the future
Nope, not even locally. Hold on lemme post a couple of files
So this is my index.html
file and normally it works when I run it with shadow-cljs
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/css/style.css" rel="stylesheet" type="text/css">
<link rel="icon" href="">
<title>shadow reagent</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
<script src="/js/main.js" type="text/javascript"></script>
<script>window.onload= function() { client.core.main(); }</script>
</body>
</html>
My server on the other hand:
(ns server.core
(:require [ :as io]
[clojure.string :as str]
[clojure.pprint :refer [pprint]]
[ring.adapter.jetty :as jetty]
[ring.middleware.reload :refer [wrap-reload]]
[ring.middleware.resource :refer [wrap-resource]]
[ring.util.response :refer [response
response?
content-type
redirect
file-response
resource-response]]
[reitit.ring :as reitit-ring]
[server.routes :refer [router]]))
...
(defn resource-handler
[request]
(as-> request r
(:path-params r)
(:filename r)
{:status 200
:body (io/input-stream (str "public/index.html" r))}))
...
;;;; Main
(def PORT
"Heroku requires that the server uses $PORT.
See "
(try
(Integer. (System/getenv "PORT"))
(catch Exception e
(println (str "Caught exception: " (.getMessage e))))))
(defn -main
[& args]
(jetty/run-jetty (-> resource-handler
(wrap-reload))
{:port (or PORT 3000)
:join? false}))
So when I run -main
, it does serve the index.html
file as I specified it
But it just displays a blank page, as opposed to when I run it with shadow-cljs where it displays the entire UI
I suspect the issue is areound (io/input-stream (str "public/index.html" r))
it's probably producing a string like "public/index.htmlindex.html"
Ahh, hmm, but it does serve the HTML file as is, though? I checked the source in a browser and it does correspond to the contents of index.html
(defn resource-handler
[request]
(prn "filename" (-> request :path-params :filename))
(as-> request r
(:path-params r)
(:filename r)
{:status 200
:body (io/input-stream (str "public/index.html" r))}))
Ahhh hold on
I'm not sure what middleware is hooked up, but I wouldn't be surprised if there wasn't any data located at (-> request :path-params :filename)
I wrote simple web app. Here's the route I use for serving files from the app's resources: https://github.com/phronmophobic/wavelength_helper/blob/main/src/wavelength_server/core.clj#L91
Thank you!
also the routes for serving the html file:
(GET "/index.html" [] (response/resource-response "index.html" {:root "public"}))
(GET "/" [] (response/resource-response "index.html" {:root "public"}))
using the built in resource-response
and :root
argument helps protect against serving other files not inside the "public" folder
I see. All right, I'll try that. I'm actually using reitit
though but it should be straightforward to translate your approach.
All right, so now I have a different problem. I am able to serve static files but for some reason my changes aren't reflected. It's like ring-jetty
is caching my files on creation then just using those.
The reason I think this is the case is because I tried deleting a file and yet both a cache-cleared Firefox and curl
still return it
I have also rebooted, hoping that it would help, to no avail
In particular, recompiling with shadow-cljs
produces a new public/js/main.js
file, but those changes aren't reflected anywhere
My question is, is there a way to disable this 'caching' behaviour?
Are the new versions of main.js
named with their release version? (Either something you specified or their git sha.)
If so, do the build tools have a way of updating your html files to point to those newly named files, or is that a manual process?
If you have it set up to not use that release-version behavior and instead to always generate main.js
exactly as spelled, then could you try changing that behavior and following the same deploy steps and see if that fixes it?
If that fixes it, at least you narrow down that it's a caching issue in a post-deploy section of the process.
I'm not familiar with Heroku so I don't know what cache-related behavior/solutions are there.
I'm pretty sure something like a cache in ring-jetty wouldn't persist after a restart of the application. That would just be an in-memory cache, right?
Hi, is there a way to invoke a no-params function without using parenthesises, I mean execute a function by using another function? e.g.
(-> #(println "hello") invoke)
Some type of invoke
function?
I mean I found I could use apply with nil,
(-> #(println "hello") (apply nil))
but still I’m wondering is there anything built-in to execute no-params functions…Well there is nothing stoping you from writing ^that, I’m just wondering in what cases it would be useful ^^
(was about to type the same as yuhan ^^)
(defn invoke [xf & args] (apply xf args))
Actually this does the job(-> #(println "hello") (.invoke))
also works for multiarity functions as well
(-> #(println "hello" %&) (.invoke 1 2 3))
thanks
having an issue running planck on a localhost port & evaluating blocks in Atom. I use planck -n 777 to start the REPL, then "Connect To Socket REPL" in Atom- I get a success message initially, but when I try to evaluate a block, I get an error. Any ideas? (uploaded images follow the timeline of actions)
@esciafardini Maybe as a test separately try telnet 0 777
(or some other TCP client) just to be sure you can connect
getting similarly conflicting response... nc: connectx to localhost port 777 (tcp) failed: Connection refused Connection to localhost port 777 [tcp/multiling-http] succeeded!
can you try with a port greater than 1024? I think these ports require more privileges to use. Try with port 7777?
same issue on port 7777
**thank you for suggestion
$ telnet 0 777
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
cljs.user=> (+ 2 3)
5
cljs.user=>
same here, must be an issue in Atom.
âžś ~ telnet 0 7777
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
cljs.user=> (+ 2 3)
5
cljs.user=>
Everything works in Atom if I use this instead of planck: clj -J'-Dclojure.server.repl={:port,5555,:accept,clojure.core.server/repl}'
@esciafardini there is a channel #chlorine-clover and @U3Y18N0UC (chlorine's author) is usually active there.
awesome thanks
What would be a good construct to use as a "fire-and-forget" action to happen on another thread, i.e., I want to execute this function (that has side effects), but I don't care if it works or not, just go off and do it in another thread (as the side effect can take a few seconds to action)
right, my thoughts too, so I dont need to deref it ever...will the future be cleaned up once the function finishes?
No one will refer to it
Gotta say, that working with threads in this manner is so nice, Clojure makes it easy 🙂
Good morning. i have the a case where a spec returns false from a valid? call, but does not provide any context on what failed with an explain. I can provide details in a thread here if anyone is familiar with spec.
(s/def ::element-shape?
(s/and
(s/map-of keyword? some? :count 7)
(s/keys :req-un [::is-uuid?
::is-name?
::is-short-desc?
::is-long-desc?
::known-behavior?
::is-config?])))
(def mock-element
{:id (str (random-uuid))
:name "mock-element-1"
:short-desc "Mock Input Element 1"
:long-desc "This is a mock element which is intended to be used for event input consumption."
:behavior :input
:component :square
:config {}})
(s/valid? ::element-shape? mock-element)
=> false
(s/explain ::element-shape? mock-element)
=>
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-uuid?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-name?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-short-desc?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-long-desc?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :known-behavior?) spec: :eventflow.ui.flow.elements.repo/element-shape?
{:id "a76406e2-21b7-4cc1-b293-529e2a5bfef0", :name "mock-element-1", :short-desc "Mock Input Element 1", :long-desc "This is a mock element which is intended to be used for event input consumption.", :behavior :input, :component :square, :config {}} - failed: (contains? % :is-config?) spec: :eventflow.ui.flow.elements.repo/element-shape?
nil
omitted all of the individual defined functions, but have confirmed the work individually and are evaluated in the repl
The explain
is telling you that all those required keys are missing from the data — and it is correct.
oh crap! i missed the word "failed" in the output
embarassed!
too much coffee, or not enough
Your s/keys
spec says that :is-uuid?
etc should be keys in the data.
But the keys in your data are things like :id
etc.
yes, you're correct. Im only about 2 hours into the :: namespace macro use - still wrapping my brain around it
let me back up my comprehension to a more basic point. If I have the following:
(s/def ::is-uuid?
(s/and string? #(re-matches uuid-regex %)))
the :: expands to the current namespace + local binding, no?
you can evaluate ::is-uuid?
in your repl and you'll see exactly what the reader expands it to. What do you mean local binding though?
thanks, that repl eval illustrated it for me
it's been years, but i think i remember a situation where using a function as a spec would fail when calling valid? but not when calling explain. I think the docstring was updated to reflect this rather than allowing functions in the spec. Let me see if i can dig it up
qp=> (require '[clojure.spec.alpha :as s])
nil
qp=> (s/def ::vector vector?)
:dev.nocommit.qp/vector
qp=> (s/valid? (s/coll-of any? :kind ::vector) [])
false
qp=> (s/explain (s/coll-of any? :kind ::vector) [])
Success!
nil
`thanks for that. my issue was with poor reading comprehension
replied to my self above with some truncated but illustrative example
do clojure developers prioritize having just one data model in a system? is the idea that we'd chose the 'correct' data model for each component considered harmful?
I think https://www.slideshare.net/mtnygard/architecture-without-an-end-state paints a good picture of the world clojure is designed to thrive in, a world where there is no single system of record, and no single model
if you google around there are videos of the talk that goes with those slides too
I certainly try to achieve as much as possible with one canonical store of data, but just recently I invoked having 2 atoms instead of one for a dataset to ensure lookups would be fast if the datasets were to get enormously large. So in almost all cases I prefer one canonical representation / store of data, but some cases it's smarter or easier to flex provided you update them in a synchronous way. The potential to include bugs or glitches with multiple data representations is greater. There is the react adage "The View is a Function of the State" for the UI and the underlying representation in web programming -- and I think it applies on a lot of levels for computing and UI. Mainly that the methods playing on the data can be many, while the data ought be singular. Of course, it's not a hard-and-fast rule, and it makes sense to rely on whatever is best for the specific issues involved, but in general the fewer the stores of data, the fewer the inadvertent bugs due to out-of-sync'ness
A single data model is my default starting point. It helps keep the overall design of the code simple. If components of a system are very distinct or have little interaction with each other, then it seems more likely that they would be treated as individual sub-systems (or even spilt into their own system), so a specific data model would seem more appropriate. If components are highly integrated, then typically I would expect them to share a common data structure. These are my general guidelines, every system has its own constraints that need to be considered carefully.
Fewer (ideally one) data models/data structures, more functions that operate on them
"It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures." - Alan J. Perlis https://clojure.org/about/rationale
@U050KSS8M - fewer data models? or data structures?
i never thought of clojure as preferring fewer data models
i mean, sure, prefer as few as possible, but with data structures, clojure is quite opinionated and quite dogmatic - here are the handful you'll ever need. i never saw clojure as having a broad opinion about the different ways my application represents the concept of a domain entity
I think if having multiple data models is justifiable, sure; when we say “multiple data models” I understood it as having different data views/shapes onto the same model; perhaps I didn’t understand the question well enough, apologies.
Model is a highfalutin term with many abstract connotations. But in general I think you are both in agreement: creativity in how you access the data is unlimited / unbound / untethered, while the data itself is ideally only present once. (But consider the dish and the meal to be served.)
@U7EFFJG73 tell me more about the difference between data model and data structure. for example, does one data structure imply 1+ models on the data? And what does this distinction look like in code? ( if I have correctly observed the nuance)
It almost certainly depends on what you mean by data model, but I think having good, idiomatic support for namespacing makes it easier for multiple "data models" to coexist. I think that's one of the reasons clojure has found strong adoption in data processing.
That doesn't mean you should create multiple data models if you can avoid it. It probably depends on the use case.