This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-02-03
Channels
- # announcements (8)
- # aws (2)
- # babashka (16)
- # beginners (173)
- # calva (13)
- # cider (4)
- # cljfx (6)
- # cljs-dev (108)
- # clojure (63)
- # clojure-australia (2)
- # clojure-dev (10)
- # clojure-europe (73)
- # clojure-italy (8)
- # clojure-nl (4)
- # clojure-norway (5)
- # clojure-uk (4)
- # clojurescript (49)
- # clojureverse-ops (4)
- # community-development (3)
- # core-async (23)
- # cursive (3)
- # data-science (5)
- # datomic (25)
- # emacs (3)
- # events (1)
- # fulcro (13)
- # helix (5)
- # introduce-yourself (1)
- # lein-figwheel (1)
- # lsp (36)
- # malli (1)
- # meander (2)
- # membrane (4)
- # music (8)
- # nextjournal (51)
- # off-topic (47)
- # other-languages (5)
- # pathom (31)
- # pedestal (5)
- # planck (14)
- # polylith (5)
- # portal (1)
- # re-frame (30)
- # react (2)
- # reagent (24)
- # releases (1)
- # rewrite-clj (18)
- # ring (9)
- # sci (33)
- # shadow-cljs (49)
- # testing (3)
- # tools-build (21)
- # tools-deps (29)
- # vim (19)
- # web-security (1)
- # xtdb (12)
hi, i'm working on a simple website using Luminus and reframe. i'm having trouble figuring out how to retrieve entries inside a database from my backend and display them as a list on the frontend. in the luminus tutorial i read, it's done by passing the results of a database call to a selmer/render-file call, which in turn allows the template file specified in the render-file call to access those results. however, i'm not sure how to utilize this if i am using reframe to generate the frontend (which works by adding a script file generated by cljs to the template file) or if i'm supposed to use other means of retrieving those results. any help would be appreciated; thanks in advance
When using re-frame (or, for example, react if you were writing in Javascript), you'll want to have an endpoint in your server that re-frame calls
And that endpoint would return the data read from the server, in json format (or edn, etc)
Another approach would be to have the backend expose the data as an API and a separate reagent/re-frame app for the front end.
this is tangential to my question above, but i'm also rethinking about using Luminus to build a web application. i've come across this site: https://www.fullstackclojure.com/ which recommends using Compojure to create a web application, and i assume there are other viable options out there. how do you recommend i choose the proper means of creating a web application? i'm also curious about any general thoughts here.
@ashbadine91 If you are just getting started with Clojure, I strongly recommend building a small server-side web app using just Ring and Compojure to gain an understanding of how Clojure deals with the web's HTTP request/response idiom. From there you can go on to build a simple REST-like API that allows a client (such as curl
at the command line) to make requests and get data back as JSON. Then you can start in on the ClojureScript side of things and build a small client web app that makes those REST requests and deals with the JSON it gets back.
Luminus bundles together a lot of moving parts and if you don't understand how the basics work, you'll flounder when trying to adapt a basic Luminus project to your needs. I do not recommend Luminus for beginners.
thanks a lot for the project suggestions sean. it's good to get a sense of direction since i've felt fairly lost on how to get a website working using clojure
Yeah, it can be a bit overwhelming because Clojure favors composing small libraries over the structured frameworks that many other languages have. It makes it much harder for beginners to get up and running in Clojure as far as web apps are concerned, but once you get used to it, assembling just what you need for any given solution is pretty straightforward.
Are there any books/resources you would recommend for learning Clojure+ClojureScript as a supplement to actually using the languages? I only read https://www.learn-clojurescript.com/, which I thought would be enough but now I'm having second thoughts.
I don't know much about ClojureScript resources, but I generally recommend Getting Clojure and Clojure Applied as two solid books for the Clojure side of things. There's a long list here https://clojure.org/community/books but my personal advice is to stick to books from Pragmatic primarily, with maybe some from O'Reilly and/or Manning, and avoid Packt.
If you want to look at a small, database-backed, server-side web app that uses several libraries but is aimed at beginners: https://github.com/seancorfield/usermanager-example/ -- the README links to a variant built with Integrant and Reitit (instead of Component and Compojure).
That variant, in turn, links to a version with a SPA front end, using Inertia.js and some middleware (I have no experience with that). At some point, I will get back to cljs and build a version of that usermanager example that has a re-frame front end and an API built into the backend. Some day...
Ok so I'm trying to use prismatic/dommy just to learn how to use clojure libraries. I am following the learn-clojurescript book. One example has
(gdom/setProperties password #js {"type" "password"})
How would I write this using dommy? I required dommy.core :as dommy. I tried
(dommy/set-style! password :type :password)
I can't get the formatting write for this and idk where to find an example.I have
(let [password (dommy/create-element :input)
Can I make it a "password type" here? I want it to show up as black dots when typed 😕 And also just learn how to translate between doing the same things in different libraries & how to figure out the correct formattingOk I figured out I do
(dommy/set-attr! password :type "password")
and it worked!Can someone explain this to me
(ns toy-project1.subs
(:require
[re-frame.core :as re-frame]
[clj-time.coerce :as timec]))
(re-frame/reg-sub
::text
(fn [db] (timec/from-long (get-in [:current :dt] db))))
Error :
[:app] Configuring build.
[:app] Compiling ...
[:app] Build failure:
The required namespace "clj-time.coerce" is not available, it was required by "toy_project1/subs.cljs".
"clj_time/coerce.clj" was found on the classpath. Maybe this library only supports CLJ?
Have you added clj-time
to you lein project.clj
or deps.edn
?
yes
{:source-paths
["src"]
:dependencies
[
[reagent "1.1.0"]
[re-frame "1.2.0"]
[cljs-ajax "0.8.4"]
[day8.re-frame/http-fx "0.2.4"]
[clj-time "0.15.2"]
]
:dev-http {8280 "resources/public"}
:builds
{:app {:target :browser
:output-dir "resources/public/js/compiled"
:asset-path "js/compiled"
:modules
{:app {:init-fn toy-project1.core/init}}
}}}
@U0EGWJE3E clj-time is a wrapper for Joda Time and is meant for Clojure side of things. You'd want to take a look at cljs-time.
Moreover, clj-time is deprecated. See here: https://github.com/clj-time/clj-time
and the code to this
(ns toy-project1.subs
(:require
[re-frame.core :as re-frame]
[cljs-time.coerce :as timec]))
(re-frame/reg-sub
::text
(fn [db] (timec/from-long (get-in [:current :dt] db))))
but still this error :
:app] Compiling ...
[:app] Build failure:
The required namespace "cljs-time.coerce" is not available, it was required by "toy_project1/subs.cljs".
Have you restarted your repl?
And actually, things have changed a bit since I last looked at this. Cljs-time is a bit old library in itself, so something else might be better. Something like https://github.com/juxt/tick or direct usage of Google Closure library goog.date
might work better.
@U0EGWJE3E what do you mean with you do not see the time? The code above isn't showing the time. It registers a subscriber for extracting data from the app database that your views can then use.
i have this subscription :
(ns toy-project1.subs
(:require
[re-frame.core :as re-frame]
[cljs-time.coerce :as timec]))
(re-frame/reg-sub
::time
(fn [db] (timec/from-long (get-in [:current :dt] db))))
and this in my template
(ns toy-project1.views
(:require
[re-frame.core :as re-frame]
[toy-project1.subs :as subs]
[toy-project1.events :as events]
))
(defn main-panel []
(let[text (re-frame/subscribe [::subs/time])]
[:div
[:div
[:button {:on-click #(re-frame/dispatch[::events/fetch-weather])} "Get weather data "]]
[:div text]]))
and I see the button but not the timeYou need to deref the subscription with @(re-frame/subscribe [::subs/time])
That will get you the value stored in the ratom. However, in your case that data is nil. There is no time.
I know that this is the returned json which is converted to a vector
{
"lat": 33.44,
"lon": -94.04,
"timezone": "America/Chicago",
"timezone_offset": -21600,
"current": {
"dt": 1618317040,
"sunrise": 1618282134,
So, somewhere, you need to initialize that data into the database. Moreover, you might wanna take a look at re-frame documentation. The layer 2 subscribers should be just that, a data subscribers. The data parsing should happen in a computation function. I.e. something like this:
(re-frame/reg-sub
::time
(fn [db _] (get-in [:current :dt] db))
(fn [dt _ _] (timec/from-long dt)))
Your data seems fine. In any case, check how and when you write the data into the database and remember to deref with @
.
Yes. The two steps are an optimization. The layer 2 query fns are rerun every time the data in app db changes. Because of this, we want them to be trivial. Just read and return data.
Here's an example and further explanations: https://github.com/day8/re-frame/blob/master/examples/todomvc/src/todomvc/subs.cljs
(get-in [:current :dt] db)
seems not quite right
@U0178V2SLAY what do you then think it should be ?
(get-in db [:current :dt])
what does your ::events/fetch-weather
event handler look like?
or rather how is the data written to the app-db when you receive it as json?
(re-frame/reg-event-db
::http-fail
(fn [db [_ key-path]]
(assoc-in db key-path {})))
(re-frame/reg-event-db
::http-success
(fn [db [_ key-path result]]
(assoc-in db key-path result)))
(re-frame/reg-event-fx
::fetch-weather
(fn [_ _]
{:http-xhrio
{:method :get
:uri ""
:params {:lat (:lat config/home)
:lon (:lon config/home)
:units "imperial"
:appid config/open-weather-api-key}
:response-format (ajax/json-response-format {:keywords? true})
:on-success [::http-success [:weather]]
:on-failure [::http-fail [:weather]]}}))
perhaps the path you use with get-in
should be [:weather :current :dt]
?
Then I see this error :
Uncaught Error: Objects are not valid as a React child (found: object with keys {date}). If you meant to render a collection of children, use an array instead.
I think you're getting closer
I’m afk for a moment, I’ll give you a few tips later
So I think your subscription is working now, or at least it's returning some kind of a datetime object. However, reagent doesn't know how to represent it in html. That's why you get a "Objects are not valid as a react child" error when you try to use it as [:div text]
.
So the next step is to turn that datetime object into something suitable, e.g. a string containging the date and time in some nice format
cljs-time
has a function called to-string
, which offers one such way to represent the datetime as a string
so perhaps try what (fn [db] (timec/to-string (timec/from-long (get-in db [:current :dt])))))
does and if it helps
[:div (timec/to-string text)]
would work also I guess, as long as you remember to require it as [cljs-time.coerce :as timec]
normally modifying the cljs code doesn't require restarting the server
I had to do (fn [db] (timec/to-string (timec/from-long (get-in db [:weather :current :dt])))))
well, at least we got something working 🙂
typically the epoch (starting time) of various time systems is midnight 1.1.1970
see this page : https://openweathermap.org/api/one-call-api
when you work with timestamps and see a value that's close to 1.1.1970 but perhaps a few days later -> somewhere there's a mixup between "seconds after 1.1.1970" and "milliseconds after 1.1.1970"
from-long
says ""Returns a DateTime instance in the UTC time zone corresponding to the given number of milliseconds after the Unix epoch.""
but the value from your API looks like it's seconds after the epoc, not milliseconds
so, what we need to do is convert that to ms -> (fn [db] (timec/to-string (timec/from-long (* 1000 (get-in db [:weather :current :dt]))))))
yeah, correct
great!
working with time zones is a constant pain. I'm not very familiar with cljs-time
so I don't know what kind of tools it provides. One possible hack to solve your problem: that API response you get contains a "timezone_offset": -21600,
-field. I guess that's the offset in seconds between the UTC timestamp and the local date. Perhaps you could add that offset to the original timestamp (before you multiply it by a 1000).
thanks, I take a break right now nothing is chancing
(fn [db] (timec/to-string (timec/from-long (- (get-in db [:weather :timezone-offset]) (* 1000 (get-in db [:weather :current :dt]))))))
Hi, is there a way to use async/chan
to implement a publish/subscriber model with multiple subscribers of a single event?
have you looked at pub
and sub
?
awesome! thanks @alexmiller
(also, feel free to ask questions in #core-async in the future)
Hello, I'm not sure if this should be an SQL channel question since it is more relevant to the database connection: Basically, I'm using HugSQL, next.jdbc to interact with PostgreSQL, sth like this:
;; database
(def db
;; dbtype and dbname are preferred to be drop-in
;; replacement for subprotocol and subname
{:dbtype "postgresql",
:dbname "name",
:user "user",
:password "passwd"})
(def ds (jdbc/get-datasource db))
;; connection and CRUD
(hugsql/def-db-fns "path/to/orders.sql"
{:adapter (next-adapter/hugsql-adapter-next-jdbc)})
Then I can run a fn, say, (add-an-order db {:order_num "xxxx", :order_note "yyyy"})
to insert a record into the database.
My question is, when I run such fns (generated by HugSQL), they will try to connect to the database I defined. Is this a only-once connection that will be closed after the sql statement is done?
Or every time a long-time connection will be created, stay there even after the fn finishes, and take some RAM until I find I'm running out ram? I think the connection is of the first type: short-time and closed once data gets inserted. Where could I find more info on this? Thank you for your helpI'd suggest asking in #hugsql -- I'm not sure how it manages connections. The recommended best practice is to set up a connection pool (which next.jdbc
supports directly, using HikariCP or c3p0) and then pass that around so that operations that do getConnection()
and then close()
are using a pool of cached connections instead of standing up a new connection to the DB every time.
It's normal to set up a connection pool at the start of your program and leave it in place until your program shuts down @U02UW9X8DUG
Thank you @U04V70XH6. Really helpful advice. I'll try out the tips you mentioned.
Hi@U04V70XH6, I have a quick question regarding hikari-cp
(a Clojure wrapper to HikariCP) and next.jdbc
.
I read next.jdbc
's doc and find it uses HikariCP directly without the wrapper.
When I tried to use the wrapper, it creates a datasource using its own db-spec hash map, where next.jdbc
's :dbtype
is declared as :adapter
. Other differences include :user
(next.jdbc) vs :username
(hiker-cp) and :dbname
vs :database-name
Passing a connection pool created by hikari-cp to next.jdbc/get-connection
will throw me an error: Unknown dbtype:, and :classname not provided.
I guess there is some collision. Since next.jdbc works with HikariCP well, I'll just drop the wrapper lib. But I'm curious if my guess is right. Wondering if you happen to know anything about this?
https://github.com/tomekw/hikari-cp#usage
A map function makes a call to an external api with account ids. When I print the the structure [accountid, apiresult] it's printed separately, asynch.
ie [account1 ""] [account2 ""] and then "apiresult1" "apiresult2"
How can I group the output together?
mapping is lazy and that's usually not what you want when interacting with and API or other side effects like printing what happens is that the data isn't calculated until the data structure is inspected by the repl print operation, so you get a weird interleaving of printing of debug info from your code and printing of data structure contents by the repl if your main goal is to see the account and result together, it's better to write your function so it joins the two into data, and then print the resulting data eg:
(defn get-api-result [input]
{:request-data input
:result (do-query input)})
instead of printing inside the get-api-result function, print what it returnsIs there a better way of doing this? (filter #(clojure.string/ends-with? full-url %) ["
The idea is to allow through a full-url, only if it ends with any of a matching whitelist domain. It could return true or false. I thought about doing apply
, but couldn't quite get it to work.
you could use something like https://github.com/lambdaisland/uri and parse the urls to make the intent more clear. otherwise, it reads find to me ¯\(ツ)/¯
(filter #(clojure.string/ends-with? "" %) ["" ""])
;;=> ("")
Or did I misunderstand?user=> (import '( URI))
java.net.URI
user=> (#{"" ""} (-> "" URI. .getHost))
nil
user=> (#{"" ""} (-> "" URI. .getHost))
""
Could just use java.net.URI
maybe?Ah the problem with your approach @U4ZDX466T is that it'll fail with
or
(basically, the host part should be ignored):
However, this `(filter #(clojure.string/ends-with? "http://evil.com/foo.com" %) ["http://foo.com" "http://bar.org"]) ;;=> ("http://foo.com")` is a valid problem with my approach too!
Depending on your situation, you could consider whitelisting CIDR blocks instead, or something like that.
It's for a chat forum, so any user can post any link as text...danger will robinson danger!
@U11EL3P9U or similarly bad "<http:evil-site.com#foo.com>"
you can literally put anything after #
and the browser happily ignores it if there's no match on the target page
edit: URI is safe to use, URL does a network query for hash-code / equality checks
Hi I'm doing the learn-clojurescript book. In one lesson we make a weather conversion widget. It says to try adding a button that will reset the input-box where you type in the tempurature to be converted. I'm trying to do something like
(defn reset-temp (gdom/setTextContent temp-input ""))
and
(gevents/listen reset-button "click" reset-temp)
And it's not working. Any ideas what I am doing wrong?
Oh I see. I have
(defn reset-temp [_] (gdom/setTextContent temp-input ""))
I didn't copy it over correctly
In index.html I have
<div>
<input type="reset" id="reset-button">
</div>
Then this is my whole file:
(ns learn-cljs.temp-converter
(:require
[goog.dom :as gdom]
[goog.events :as gevents]))
(defn f->c [deg-f]
(/ (- deg-f 32) 1.8))
(defn c->f [deg-c]
(+ (* deg-c 1.8) 32))
(def celsius-radio (gdom/getElement "unit-c"))
(def fahrenheit-radio (gdom/getElement "unit-f"))
(def temp-input (gdom/getElement "temp"))
(def output-target (gdom/getElement "temp-out"))
(def output-unit-target (gdom/getElement "unit-out"))
(def reset-button (gdom/getElement "reset-button"))
(defn get-input-unit []
(if (.-checked celsius-radio)
:celsius
:fahrenheit))
(defn get-input-temp []
(js/parseInt (.-value temp-input)))
(defn set-output-temp [temp]
(gdom/setTextContent output-target
(.toFixed temp 2)))
;; 5 event handling callback
(defn update-output [_]
(if (= :celsius (get-input-unit))
(do (set-output-temp (c->f (get-input-temp)))
(gdom/setTextContent output-unit-target "F"))
(do (set-output-temp (f->c (get-input-temp)))
(gdom/setTextContent output-unit-target "C"))))
(defn reset-temp [_]
(gdom/setTextContent temp-input ""))
;; 6 attach event handlers
(gevents/listen temp-input "keyup" update-output)
(gevents/listen celsius-radio "click" update-output)
(gevents/listen fahrenheit-radio "click" update-output)
(gevents/listen reset-button "click" reset-temp)
I wish to encrypt files with AES (256 bit key, CBC mode) using streams (more specifically file streams), and i don't wish to reinvent the wheel so i'm wondering if there is an existing library that'd allow me to do that?
look at Cipher and CipherOutputStream in the JDK https://docs.oracle.com/en/java/javase/17/docs/api/java.base/javax/crypto/Cipher.html
okay thanks
I’d like to perform the job of a reduce
which produces a sequence. In this case where each element in the resulting sequence depends in some arbitrary way on the previous element in the resulting sequence and an element in the reduced over collection. I can do this with reduce, no problem, but I would like to do this lazily, i.e. so that the calculation for a specific element in the resulting collection is only performed on request.
Any thoughts on how to accomplish that?
This is still kind of on the boiler in my head so I might be foobaring something in my thinking…
A common idiom that I use is:
(map (fn [prev current] (f prev current))
xs
(rest xs))
one note is that it will only call the mapping function for every element that has a previous. If you want to call it for every value, you can do something like:
(map (fn [prev current] (prn prev current))
xs
(concat (rest xs) [nil]))
hmm…this was to replace something like:
(reduce
(fn [[a p] v] [(conj a (operation p v)) v])
[[] nil]
coll)
i.e. a reduce which reduces over a collection, but also depends on the previous value of the resulting collection (`a` above)hmm…seems I can probably do this somehow via reductions…ok think I have enough to keep digging. Thanks for the pointers!
> i.e. so that the calculation for a specific element in the resulting collection is only performed on request as a side note, if you want to avoid doing any unnecessary work reduction, then lazy sequences can sometimes get you into trouble due to chunking. If doing a little extra work isn't an issue, then the lazy sequence versions should work fine. If it is an issue, you may want to explicitly apply the reducing function yourself on request.
(reduce (fn [acc [prev current]]
(conj acc (operation prev current)))
init
(partition 2 1 coll))
or what you had already with the composit accumulator destructure(defn foo
[op init coll]
(if (< (count coll) 2)
()
(lazy-seq
(let [x (apply op (take 2 coll))
acc (conj init x)]
(cons acc
(foo op acc (rest coll))))))
lazy-seq version, but I think you only want the last value so reduce is betterand I guess reductions is still better if you need the transitory values in between - lazy-seq requires too much sequence walking boilerplate so it's much less clear
There's also a reductions transducer in the xforms library, then you wont have to worry about chunking
thank you all for the input. Specifically for this particular case I was looking to calculate various trend metrics for finance. Exponential moving average is an example, i.e. you get a coll/seq in, you produce a new coll/seq as output based on a calculation which depends on current element and last calculated value. For ema specifically it seems reductions
is beautiful:
(defn ema [^long n coll]
(let [a (/ 2 (inc n))]
(reductions
(fn [pv v] (+ (* a v) (* (- 1 a) pv)))
(first coll)
(rest coll))))
(ema 6 (range 5))
=> (0 2/7 38/49 484/343 5164/2401)
I’ve run across reductions a few times and never really saw the light…clojure keeps expanding the mind.where the first argument is the length of the coll, I do this as my actual function has a transducer arity and I think the transducer needs to be told the length of the input as it can not do “global” operations on the indata
anyway…thanks @U051SS2EU, @U7RJTCH6J and @UK0810AQ2! Will hold on to your notes as I’m sure I will have use for them over the course of this project.
@UK0810AQ2 when using lazy-seq
directly I don't think chunking comes into play? it's higher level stuff like map
that respects chunks I thought
yeah - no worry about chunks when using lazy-seq directly
(cmd)user=> (defn lazy-map
[f coll]
(lazy-seq
(when (seq coll)
(cons (f (first coll))
(lazy-map f (rest coll))))))
#'user/lazy-map
(ins)user=> (take 1 (lazy-map println [1 2 3]))
(1
nil)
(cmd)user=> (take 1 (map println [1 2 3]))
(1
2
3
nil)