This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-12-19
Channels
- # adventofcode (18)
- # announcements (1)
- # babashka (153)
- # beginners (73)
- # bristol-clojurians (4)
- # calva (1)
- # cider (6)
- # clj-kondo (38)
- # clojure (154)
- # clojure-dev (12)
- # clojure-europe (7)
- # clojure-finland (11)
- # clojure-nl (70)
- # clojure-spec (13)
- # clojure-uk (101)
- # clojuredesign-podcast (2)
- # clojurescript (15)
- # core-async (30)
- # cryogen (1)
- # cursive (5)
- # devops (1)
- # duct (4)
- # figwheel-main (1)
- # fulcro (19)
- # jobs (12)
- # kaocha (17)
- # luminus (2)
- # malli (8)
- # music (5)
- # nrepl (13)
- # off-topic (20)
- # overtone (3)
- # re-frame (7)
- # reagent (38)
- # shadow-cljs (13)
- # specter (3)
- # tools-deps (6)
- # vim (7)
is this on linux? because then you could just syscall to chown. i don't know if there's a concept of numeric user id on windows
Im doing a chown atm. But it stands out like a sore thumb. Wondering if there is a way in pure java
https://docs.oracle.com/javase/8/docs/api/java/nio/file/attribute/UserPrincipalLookupService.html
I'm building web form validation using malli. Without having fully grasped the model yet, I still think I will want to stick with it. A problem I have is figuring out how to express a schema where a check depends on another input field. Say I have a question A with allowed answers :yes
and :no
, then I have a question B that should be validated only if A has answer :yes
. How should I express that?
@kwladyka, I have. 😃 It is my inspiration for what I am doing now, but using malli instead. I like that it is data. (Which incidentally lets me validate my A and B form by dynamically updating the schema, but that is another story.)
Can you give an example what it can do what spec can’t? I saw malli once, but I didn’t use it.
I'm not familiar enough with either to have that discussion, hopefully someone else is. To me it is enough that it is data rather than macros. It just fits my conceptualisation of things better.
> A problem I have is figuring out how to express a schema where a check depends on another input field. Say I have a question A with allowed answers :yes
and :no
, then I have a question B that should be validated only if A has answer :yes
. How should I express that?
I don’t know malli, but for spec the best choice is to use fn
to compare different data, because on the end you want to know which field failed and where to show error message.
> you want to know which field failed and where to show error message.
Indeed, that is what I need. With malli I have found a way to do the conditional validation (using fn
, as you suggest), but I loose the information about which field failed.
My efforts might turn out in a library, but that would take that I feel I understand the problem much better than I do today.
I think you should use fn
without shema, unless malli solved it in different way. But from what you are saying it is not solved.
In my solution I use this https://github.com/kwladyka/form-validator-cljs/blob/doc/src/form_validator_doc/views.cljs#L12-L19 in doc
My validation is so far strictly using malli validations in a citrus controller, and then my rum components update as a reaction. If I can, I will try to keep it like that. But we'll see.
My control looks like so, btw:
(defmethod control :ui-form-validate [_ [form-key schema defaults] state]
(let [error-messages (human-errors-for schema (merge defaults (get-in state [:ui-form-values form-key])))]
{:state (-> state
(assoc-in [:ui-form-error-messages form-key] error-messages))}))
For completeness:
(defn human-errors-for
"Valiadates `data` against `schema` and returns human consumable errors mapped to the fields."
[schema data]
(-> schema
(m/explain data)
(me/humanize {:wrap :message})))
What IDE and OS do you use?
For “serious projects” IntelliJ IDEA & Cursive but I like Calva for adhoc hacking (macOS).

intellij + cursive + linux
Intellij - Cursive - Ubuntu
Emacs + Mac
spacemacs + mac/arch
Emacs/Cider on RedHat Enterprise 6 (with fvwm) for work, and on Slackware (with i3wm) for home.
What is the recommended way to deal with separate routes for site and API using ring and bidi? I want to wrap sites with (wrap-defaults sites-defaults) but API routes not. I used to generate a handler for all routes using bidi/make-handler and then wrap everything with the site middle ware. Not sure how to combine multiple bidi/make-handler. I am also testing to wrap every route independently, but I am getting an error with CSRF tokens. (It seems like every route has different CSRF token.)
@pez Schemas are just data, many ways:
1) create different versions of the schema programmatically
2) add the :b
as [:b {:optional true} any?]
so that it allows anything and put a top-level :fn
to validate it from one level up
3) just add a top-level :fn
and mark it’s position with :error/path
property (will look like it originates from field :b
.
Not sure what is the simplest, but here’s the solution with 1:
(require '[malli.core :as m])
(require '[malli.error :as me])
(defn form [{:keys [a]}]
[:map
[:a [:enum "yes" "no"]]
(if (= "yes" a) [:b boolean?])])
(defn humanize [data]
(-> (m/explain (form data) data)
(me/humanize {:wrap :message})))
(humanize {})
; => {:a "missing required key"}
(humanize {:a "no", :b "INVALID"})
; => nil
(humanize {:a "yes", :"INVALID"})
; => {:b "should be boolean"}
here’s the same with 3:
(-> [:and
[:map
[:a [:enum "yes" "no"]]]
[:fn {:error/path [:b]
:error/message "a was true so b should be boolean!"}
'(fn [{:keys [a b]}]
(if (= a "yes") (boolean? b)))]]
(m/explain {:a "yes", :"INVALID"})
(me/humanize {:wrap :message}))
; => {:b "a was true so b should be boolean!"}
Spring is an app framework, I've used it with Clojure, but its more using Clojure inside Spring
@kenny Depends what you mean by "valid keyword". The keyword
function accepts pretty much anything as its argument but the reader is much stricter about what it will accept as a literal keyword.
Something like this, I think [A-Za-z]+[A-Za-z0-9*+\!\-_'?<>=]
. It's not quite right though.
Well, the reader will accept more than the reader reference documents so that's another variable...
I’m running into some Java reflection(?) issues I think but I’m not sure how to resolve them: More than one matching method found: readLine
user=> (keyword "/")
:/
user=> (keyword "//")
://
user=> (keyword "//://::///")
://://::///
user=> :/
:/
user=> ://
Syntax error reading source at (REPL:19:0).
Invalid token: ://
Note that :/
is accepted but is almost certainly "not allowed" by the reference docs...?also fun:
user=> (let [a (keyword "" "") b (keyword "/")] [a b (= a b)])
[:/ :/ false]
also
:
is accepted by the reader@martinklepsch paste the surrounding code
TBH, I'm just trying to prevent reading in keywords from JSON that will not print out nicely in cursive. This keyword causes Cursive to not pprint
:/{project}/global/targetSslProxies
Also :1
is acceptable to the reader but disallowed by the reference docs -- the core team tried to change that once but it broke some widely-used libraries...
(require '[clojure.data.csv :as csv]
'[clojure.java.shell :as sh]
'[clojure.string :as string])
(import '(org.jline.terminal TerminalBuilder)
'(org.jline.reader LineReaderBuilder)
'(org.jline.reader.impl.completer StringsCompleter)
'(org.jline.reader.impl LineReaderImpl)
'(org.jline.reader Candidate))
(set! *warn-on-reflection* true)
(def csv-cols [:txnidx :date :code :description :account :amount :total])
(def term (TerminalBuilder/terminal))
(def accounts
(string/split (:out (sh/sh "hledger" "accounts")) #"\n"))
(defn account-reader []
(.. (LineReaderBuilder/builder)
(terminal term)
(completer (StringsCompleter. (map #(Candidate. %) accounts))) ;; here I'm also not sure how to avoid reflection warnings
(build)))
(defn read-account []
(.readLine ^LineReaderImpl (account-reader) "> " nil nil "")) ;; this line produces the error
(read-account)
https://www.javadoc.io/static/org.jline/jline/3.13.2/org/jline/reader/impl/LineReaderImpl.html
it works in some situations but I really don’t have a good grasp on when/why
I think you need to tag the return of account-reader
: (defn account-reader ^LineReader [] .....)
hinting to a private class is incorrect hinting inline forms sometimes doesn't work because of a bug (you can lift the result of (account-reader) to a let binding, then hint the symbol
got it thanks
It seems that the error stays around though
{:deps {org.jline/jline {:mvn/version "3.13.2"}
org.clojure/data.csv {:mvn/version "0.1.4"}}}
this is my deps.edn
just running clj file.clj
for this
(require '[clojure.data.csv :as csv]
'[clojure.java.shell :as sh]
'[clojure.string :as string])
(import '(org.jline.terminal TerminalBuilder)
'(org.jline.reader LineReader LineReaderBuilder)
'(org.jline.reader.impl.completer StringsCompleter)
'(org.jline.reader.impl LineReaderImpl)
'(org.jline.reader Candidate))
(set! *warn-on-reflection* true)
(def csv-cols [:txnidx :date :code :description :account :amount :total])
(def term (TerminalBuilder/terminal))
(def accounts
["test1" "test2" "test3" "test4"])
(defn account-reader ^LineReader []
(.. (LineReaderBuilder/builder)
(terminal term)
(completer (StringsCompleter. (map #(Candidate. %) accounts)))
(build)))
(defn read-account []
(.readLine (account-reader) "> " nil nil ""))
(read-account)
I think this should work as is (removed the sh
stuff)
Thanks for your help already 🙂
(defn account-reader ^LineReader []
(.. (LineReaderBuilder/builder)
(terminal term) (completer (StringsCompleter. ^java.lang.Iterable accounts)) (build)))
strings completer takes an Iterable<String> ^ so just pass the lazy-seq, which is already iterable
right, I tried just passing the seq but couldn’t get that to work, the completion would always throw an error that java.base/java.lang.String cannot be cast to org.jline.reader.Candidate
still getting the readLine
thing with your code
That makes sense (or not 🐵)
(defn read-account []
(.readLine (account-reader) "> " nil nil ""))
Syntax error (IllegalArgumentException) compiling . at (REPL:2:3).
More than one matching method found: readLine
and readLine(String prompt, String rightPrompt, MaskingCallback maskingCallback, String buffer)
the third is ambiguous
alright, I’m following 🙂
Shouldn’t that be ^Character then?
well, ideally I’d call one that just takes prompt
and buffer
but that doesn’t exist 🙂
ok, this is working now
THANK YOU!!!
pretty much if Clojure can figure out the target class, then it's an exercise in playing compiler and disambiguating
yeah, that makes sense, I’m surprised I never had to deal with that much
but I guess I’ve only used “good” Java APIs 🙂
It’s interesting to see how the types are used instead of named args
anyway, thanks for taking the time to explain, I think I’m equipped to figure out the rest 🙂
@kenny The Clojure/JVM Reader class has a regex in it that it uses to match symbols: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LispReader.java#L66 For almost any purpose you can imagine, there are going to be differences between any particular regex and "what you want for that purpose", but it is at least a starting point.
I believe that regex I linked to isn't the whole story, because there is Java code before and/or after using it that restricts it somewhat more.
@kenny if you are consuming strings and some of them are illformed as keywords, I'd argue the best fix is not to turn them into keywords - strings are valid as map keys
Yeah, not sure what is going to be the best path for this particular operation. I'm parsing swagger spec docs that have HTTP paths as map keys. Everything else is a valid keyword. So either I interact with the swagger doc always using get
with string keys or have everything except for HTTP paths be keywords. Having keys be keywords has the big win of getting editor completion help. String keywords runs the risk of misspelling something & no editor completion.
copy/paste?
The disadvantages you describe, on my first reading at least, seem small in comparison to the very likely N different situations you can run into down the road with funky keywords.
Well, the funky keywords would (hopefully) be avoided by only keywordizing strings that produce valid keywords.
Add in a new linter -- it has a different expected syntax for Clojure keywords. Have a team member using a different IDE, again with its own variant and corner cases. rinse, repeat, until disgusted.
I may be imagining the problems are more than they actually are, and magnifying issues that are actually smaller.
My attitude here reflects a similar perspective I have on another software development thing; "Life is too short to learn C++". Life for me is too short to deal with keyword corner cases.
@kenny You should be able to control how the Swagger JSON is keywordized, yes? If so, use a custom function that calls keyword
for most args but leaves HTTP paths as-is? Or turns them into valid keywords using a known transformation?
Well, I was thinking of inverting that so you only need a regex for things you know you'll get from Swagger that you don't want keywordized...
But, yeah...
@kenny also, for top level keys, you can use the map as a function on the key
user=> ({"a" 0} "a")
0