Fork me on GitHub
#clojure
<
2019-12-19
>
Crispin06:12:09

anyone know how to set the ownership of a file by user id number (not user name)?

valerauko06:12:28

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

Crispin07:12:30

Im doing a chown atm. But it stands out like a sore thumb. Wondering if there is a way in pure java

Crispin07:12:01

theres Posix file attributes in java nio api... seems strange theres no uid...

Crispin06:12:28

I can only find java nio methods to do it by user name

Crispin06:12:12

there seems to be no lookupPrincipleById

pez11:12:23

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?

pez11:12:04

@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.)

❤️ 4
kwladyka11:12:55

Can you give an example what it can do what spec can’t? I saw malli once, but I didn’t use it.

pez11:12:39

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.

kwladyka11:12:09

> 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.

pez11:12:39

And, as my avatar might give away, I have a soft spot for anything Finish . 😃

4
kwladyka11:12:30

Let me know if you find anything better, than it is done with spec in my solution.

pez11:12:57

> 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.

pez11:12:14

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.

kwladyka11:12:33

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.

kwladyka11:12:39

this fn is called from my library,but it is just a fn

pez11:12:22

I think malli might have solved it, but that I have so far failed to figure leverage.

pez11:12:26

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.

kwladyka11:12:45

I am afraid I can’t help more, because I don’t know malli

pez12:12:03

Yeah, let's hope someone with malli know-how sees my question.

plexus12:12:44

Maybe try asking in #malli

pez13:12:46

Ah, thanks!

pez12:12:41

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))}))

pez12:12:47

And then I send in different subsets of the full schema to create the Ux I am after.

pez12:12:58

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})))

👍 4
Diogo Japiassu12:12:44

What IDE and OS do you use?

p-himik13:12:32

IntelliJ IDEA + Cursive on Ubuntu.

kwladyka13:12:52

IntelliJ IDEA + Cursive on OS X

Kevin13:12:04

(spac)emacs on MacOS

mping13:12:59

emacs on linux/macos

pez13:12:54

Calva on macOS. (It wouldn't be fitting if I used anything else but Calva 😃 )

calva 4
Janne Sauvala13:12:20

For “serious projects” IntelliJ IDEA & Cursive but I like Calva for adhoc hacking (macOS).

calva 8
kulminaator13:12:08

intellij + cursive + linux

plexus13:12:46

Spacemacs on Linux (Ubuntu with XMonad)

Bruno Rodrigues13:12:29

Intellij - Cursive - Ubuntu

mdxprograms14:12:38

spacemacs + mac/arch

Aaron Cummings14:12:53

Emacs/Cider on RedHat Enterprise 6 (with fvwm) for work, and on Slackware (with i3wm) for home.

mgrbyte14:12:53

emacs/CIDER - Ubuntu

👍 8
borkdude14:12:11

emacs/Prelude/CIDER - macOS mojave

chepprey15:12:46

emacs/cider, Ubuntu

bringe15:12:31

Calva/VS Code - Ubuntu and Windows

calva 4
Pavel Klavík14:12:54

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.)

ikitommi15:12:40

@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"}

ikitommi15:12:11

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!"}

pinkfrog15:12:25

does anyone have success stories about using spring with inside clojure?

potetm18:12:25

why would you do this to yourself?

didibus18:12:20

Spring from inside?

didibus18:12:16

Spring is an app framework, I've used it with Clojure, but its more using Clojure inside Spring

✔️ 4
pez15:12:54

Awesome, @ikitommi! 3 looks exactly like what I need.

kenny20:12:51

Is there a regex pattern out there for strings that could be a valid keyword?

seancorfield20:12:54

@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.

kenny20:12:31

Valid according to the reader reference

kenny20:12:04

Something like this, I think [A-Za-z]+[A-Za-z0-9*+\!\-_'?<>=]. It's not quite right though.

seancorfield20:12:04

Well, the reader will accept more than the reader reference documents so that's another variable...

martinklepsch20:12:08

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

seancorfield20:12:53

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...?

noisesmith20:12:47

also fun:

user=> (let [a (keyword "" "") b (keyword "/")] [a b (= a b)])
[:/ :/ false]

😆 4
noisesmith21:12:57

also

:
is accepted by the reader

ghadi20:12:22

@martinklepsch paste the surrounding code

kenny20:12:44

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

seancorfield20:12:48

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...

martinklepsch20:12:34

(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)

ghadi20:12:21

link to the doc page for LineReaderImpl?

martinklepsch20:12:42

it works in some situations but I really don’t have a good grasp on when/why

ghadi20:12:47

reflection gets lost when you pass nil

ghadi20:12:56

because any reference type can be nil

ghadi20:12:43

but that's not what's going on here

ghadi20:12:47

I think you need to tag the return of account-reader: (defn account-reader ^LineReader [] .....)

ghadi20:12:01

and take off the hint inside read-account

ghadi20:12:22

never hint to an *Impl or otherwise private class -- always to the public class

👍 4
ghadi20:12:23

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

martinklepsch20:12:47

It seems that the error stays around though

ghadi20:12:00

how does the code look now?

ghadi20:12:09

btw what's the deps.edn coordinate for JLine? I can try it

martinklepsch20:12:20

{:deps {org.jline/jline {:mvn/version "3.13.2"}
        org.clojure/data.csv {:mvn/version "0.1.4"}}}
this is my deps.edn

ghadi20:12:31

beautiful

martinklepsch20:12:33

just running clj file.clj for this

martinklepsch20:12:34

(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)

martinklepsch20:12:18

I think this should work as is (removed the sh stuff)

martinklepsch20:12:22

Thanks for your help already 🙂

ghadi20:12:27

(defn account-reader ^LineReader []
  (.. (LineReaderBuilder/builder)
      (terminal term) (completer (StringsCompleter. ^java.lang.Iterable accounts)) (build)))

ghadi20:12:47

strings completer takes an Iterable<String> ^ so just pass the lazy-seq, which is already iterable

ghadi20:12:01

sorry my indentation is messed up

martinklepsch20:12:48

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

martinklepsch21:12:32

still getting the readLine thing with your code

ghadi21:12:40

yeah it's not enough -- that's part 1

ghadi21:12:56

you were getting the error because lazy-seqs are both Iterable and Collection

ghadi21:12:13

but the API (foolishly?) takes an Iterable<String> but a Collection<Candidate>

ghadi21:12:45

as for the last call:

martinklepsch21:12:58

That makes sense (or not 🐵)

ghadi21:12:00

(defn read-account []
  (.readLine (account-reader) "> " nil nil ""))
Syntax error (IllegalArgumentException) compiling . at (REPL:2:3).
More than one matching method found: readLine

ghadi21:12:13

error ^ right?

ghadi21:12:35

the call has the target (LineReader) + 4 arguments

ghadi21:12:48

you know it's finding the LineReader because of the error

ghadi21:12:03

if it didn't know what the target class was, it would be a different error

ghadi21:12:14

so it's about disambiguating the 4 args

ghadi21:12:42

the api has two methods with 4 args

ghadi21:12:43

readLine(String prompt, String rightPrompt, Character mask, String buffer)

ghadi21:12:49

and readLine(String prompt, String rightPrompt, MaskingCallback maskingCallback, String buffer)

martinklepsch21:12:16

the third is ambiguous

ghadi21:12:18

you're passing in String, ??? , ??? String

martinklepsch21:12:29

alright, I’m following 🙂

ghadi21:12:31

so let bind the third arg into a symbol

ghadi21:12:54

...[^String third-arg nil]

ghadi21:12:06

or [^MaskingCallback third-arg nil]

martinklepsch21:12:17

Shouldn’t that be ^Character then?

ghadi21:12:25

yeah sorry

ghadi21:12:27

I misread

ghadi21:12:34

I'm not sure which one you're intending to call

ghadi21:12:41

so Clojure can't be sure either 🙂

martinklepsch21:12:02

well, ideally I’d call one that just takes prompt and buffer but that doesn’t exist 🙂

martinklepsch21:12:06

ok, this is working now

ghadi21:12:27

pretty much if Clojure can figure out the target class, then it's an exercise in playing compiler and disambiguating

martinklepsch21:12:59

yeah, that makes sense, I’m surprised I never had to deal with that much

martinklepsch21:12:18

but I guess I’ve only used “good” Java APIs 🙂

martinklepsch21:12:44

It’s interesting to see how the types are used instead of named args

martinklepsch21:12:03

anyway, thanks for taking the time to explain, I think I’m equipped to figure out the rest 🙂

ghadi21:12:47

there are very few "good" java apis. too many turdlets

ghadi21:12:53

and my pleasure 🙂

kenny20:12:29

I think this is the pattern #"[A-Za-z]+[A-Za-z0-9*+\!\-_'?<>=]*"

andy.fingerhut20:12:37

@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.

kenny20:12:26

Oh cool! I figured something like that had to be somewhere.

andy.fingerhut20:12:30

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.

noisesmith20:12:03

@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

kenny20:12:44

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.

andy.fingerhut20:12:16

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.

kenny20:12:13

Well, the funky keywords would (hopefully) be avoided by only keywordizing strings that produce valid keywords.

andy.fingerhut20:12:19

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.

andy.fingerhut20:12:36

I may be imagining the problems are more than they actually are, and magnifying issues that are actually smaller.

andy.fingerhut20:12:54

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.

seancorfield21:12:46

@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?

kenny21:12:41

(defn json-key-fn
  [param]
  (if (re-matches kw-pattern param)
    (keyword param)
    param))
🙂

seancorfield21:12:45

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...

noisesmith21:12:42

@kenny also, for top level keys, you can use the map as a function on the key

noisesmith21:12:59

user=> ({"a" 0} "a")
0

kenny21:12:50

Oh right. I hardly ever do that so I forgot that's a thing