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?
That doesn't parse for me
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
What are you trying to do?
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?
@smith.adriane 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
<!DOCTYPE html>
<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="<https://clojurescript.org/images/cljs-logo-icon-32.png>">
<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 [<http://clojure.java.io|clojure.java.io> :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 <https://devcenter.heroku.com/articles/dynos#local-environment-variables>"
(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))}))
ah ok
Ahhh hold on
could it be the case that I need to route /js/main.js as well?
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
I see. I'll find a way to make it serve the entire public
dir
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.
My question is, is there a way to disable this 'caching' behaviour?
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
@madstap thanks, I see it needed very complex code
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โฆ(defn invoke [f] (f))
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 jobI think it's just trivial enough not to warrant being in clojure.core, heh
(-> #(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
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)
future
right, my thoughts too, so I dont need to deref it ever...will the future be cleaned up once the function finishes?
Yes
No one will refer to it
w00t
perfecto, thank you ๐
Gotta say, that working with threads in this manner is so nice, Clojure makes it easy ๐
$ telnet 0 777
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
cljs.user=> (+ 2 3)
5
cljs.user=>
^ This is what I get
Perhaps local firewalls are in place on your box?
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}'
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.
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
(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
replied to my self above with some truncated but illustrative example
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?
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?
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
@raspasov - 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.)
@michael740 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)
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?
qp=> ::uuid?
:dev.nocommit.qp/uuid?
thanks, that repl eval illustrated it for me
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
`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.
thanks for that. my issue was with poor reading comprehension
That doesn't mean you should create multiple data models if you can avoid it. It probably depends on the use case.
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
@esciafardini there is a channel #chlorine-clover and @mauricio.szabo (chlorine's author) is usually active there.
awesome thanks
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