Fork me on GitHub
#clojure
<
2019-07-18
>
paulocuneo00:07:27

ok, so this is the full iceberg: I want to store a json recieved from the web in a nosql store, but I would like to store only the fields Im interested in. So I was planning to use jackson.

(defrecord Foo ^{JsonCreator true} [^{JsonProperty "field1"} field1])
(-> (ObjectMapper.)
    (.readValue json Foo))
I didnt fully investigate if clojure.spec can remove the unwanted field.

Joe Lane00:07:24

Why make a record?

ghadi00:07:10

use something like Cheshire or data.json to read in as data @paulocuneo

paulocuneo00:07:06

ok, that will solve the serialization issue, but how would you remove the unwanted field/nested-fields?

noisesmith00:07:07

data.json takes a json string and gives back a hash map, then you can dissoc and encode again

paulocuneo00:07:03

I was thinking of select-keys actually, writing something like a parser But since the json is kinda big it would have been the same to describe the full structure.

(defn clear-fields[{:keys[field1 field2 ...manyfields]}]
   {:field1 (clear-sub-field1 field1)
    :field2 (clear-sub-field2 field2)
   ...
})

Joe Lane00:07:32

how deep of a tree is it?

lukasz00:07:36

clojure.walk ?

paulocuneo00:07:16

maybe 4 levels deep

Joe Lane00:07:29

Is the structure known and static?

paulocuneo00:07:33

yes, it's known and static

paulocuneo00:07:14

I just want to avoid storing garbage into nosql

Joe Lane00:07:32

So it's just an optimization?

paulocuneo00:07:52

it's a security issue too

paulocuneo01:07:55

It's better to whitelist, a priori it's unknwon what an attacker would add to the json, and it's not a recursive structure. I'll go with select-keys and update-in for now. I'll looking into schema an spec. Thanks @U051SS2EU @U050ECB92 @U0CJ19XAM @U0JEFEZH6 !!!

Joe Lane00:07:02

Would it be easier to whitelist things you want to keep or blacklist things you know you don't want?

Joe Lane00:07:48

If you use a whitelist you can reduce over the collection of paths and accumulate into a map

Joe Lane00:07:02

it would be like select keys but with deeply nested paths.

noisesmith00:07:07

If the keys are predictable and are bad at all levels of nesting, it's easy to sanitize with clojure.walk

noisesmith00:07:28

user=> (clojure.walk/postwalk (fn [x] (if (map? x) (dissoc x :a :c :e) x)) {:a {:b :f} :b {:c :g :d :l} :d {:d {:d :e}}})
{:b {:d :l}, :d {:d {:d :e}}}

noisesmith00:07:28

on the other hand if the keys happen at predictable levels, update-in with dissoc might be more straightforward

andy.fingerhut01:07:30

I am trying out clj-async-profiler on a project, OS X with Oracle/Apple JDK 8, starting it with clojure command. I am trying with the option -Djol.tryWithSudo=true mentioned in clj-async-profiler's README, which causes a password prompt to appear in the terminal window where I started clojure when clj-async-profiler triggers some call that requires superuser privileges. I try to enter my password, but it seems that the clojure REPL is fighting the password prompt for the input characters, and clojure wins. It interprets my password as an unknown Clojure symbol. Any advice for improving on this?

andy.fingerhut01:07:20

I have tried running the clojure command itself using sudo clojure from the terminal, but then when it gets to the point where it would have prompted, instead I see "Killed: 9" and the clojure process dies.

andy.fingerhut01:07:51

When I do the clojure command without sudo in an Ubuntu Linux VM, it also prompts for a password, and the first time I type it and press return clojure gets it and says no such symbol, but if I immediately type it again and press return, it works. Weird.

andy.fingerhut01:07:44

I have also discovered that just as sudo is often configured by default so that if you authenticate with your password once, then within a few minutes later sudo commands do not prompt for a password, on Ubuntu Linux if I do some other sudo command, then clojure within those few minutes, it does not even prompt for a password. Nice.

andy.fingerhut01:07:11

And on OS X, if I try that same thing, it gets "Killed: 9", the same as if I run sudo clojure

andy.fingerhut01:07:46

If I rubber duck this enough, I will find a solution I am content with, probably, except perhaps for OS X 🙂

duckie 4
jumar10:07:39

Where's is this tryWithSudo option mentioned (couldn't find anything in https://github.com/jvm-profiling-tools/async-profiler or https://github.com/clojure-goes-fast/clj-async-profiler) and why is that useful?

jumar10:07:26

I use async profiler but the only thing I added to project.clj is :jvm-opts ["-Djdk.attach.allowAttachSelf=true"]

andy.fingerhut17:07:31

When I use the allowAttachSelf=true option with at least some combinations of OS and JVM versions (maybe all? I don't recall right now), it mentions the tryWithSudo option, but perhaps that is because I am running my Clojure program using a different library (Java Object Layout) that needs that permission to do things. So perhaps this is unrelated to clj-async-profiler.

lilactown04:07:04

how do I access instance fields set in constructor of a deftype?

lilactown04:07:45

e.g. in CLJS I can do:

(deftype Foo [bar])

(def foo (->Foo "baz"))

(.-bar foo) ;; => "baz"

seancorfield05:07:16

@lilactown That works in clj too so I'm not sure what your question is?

lilactown05:07:36

I think the issue is that the field I want to access is marked as volatile-mutable

lilactown05:07:48

I can’t find much docs but stackoverflow leads me to believe it’s private

Alex Miller (Clojure team)05:07:27

Any mutable fields will be made private

Alex Miller (Clojure team)05:07:15

To access, your deftype needs to extend interfaces or protocols that can expose the data (safely)

Alex Miller (Clojure team)05:07:05

Mutable stuff is inherently unsafe so is not public by default

bartuka05:07:57

I find odd that lein cloverage is assigning 100% of test coverage to a file full of specs. But I have literally 0 tests in my project. How is that?

Alex Miller (Clojure team)06:07:40

specs are all macros. maybe just compiling them is "coverage" ?

bartuka12:07:35

Maybe, I will investigate this on a empty project later on

jeroenvandijk10:07:19

I just did some first (naive) tests comparing fressian and transit. It seems Transit is more compact than Fressian. Does this sound correct? I had expected the opposite https://gist.github.com/jeroenvandijk/b963ef7438b1511ed4362cca022f68f6

mpenet10:07:28

doesn't transit cache keys in maps ? could be it. I am quite sure fressian doesn't. On the otherhand fressian allows to define your own handlers for custom types, so I imagine you can optimize things further.

noisesmith17:07:10

not just keys - any value anywhere in the input, if identical? to a previous value, is replaced by a backreference

noisesmith17:07:29

so in cases where you have duplicate entries at different points in the same input, it can shrink things drastically

noisesmith17:07:41

eg. (let [m {:a (range 10000)}] {:x m :y m m [m m m]}) has only one copy of m, and 5 back references, when encoded

noisesmith17:07:53

the back references are typically two characters long in the output

jeroenvandijk10:07:08

yeah i guess the extensibility is the prime benefit then (maybe cpu performance, but i don't care too much)

mpenet10:07:15

you could also add snappy/lz4 or someting on top of fressian

mpenet10:07:43

fressian does some optimisations (varints & co), but nothing substantial

mpenet10:07:49

as far as I remember

jeroenvandijk10:07:40

i should experiment more and see what i can get out of it. for example I havent seen this feature working yet https://github.com/Datomic/fressian/wiki/Caching

jeroenvandijk10:07:07

i hoped a repeated object would be cached but that doesn't seem to be the case

mpenet10:07:10

you can end up with quite compact representation of "types" you can control

mpenet10:07:23

ex not repeating keys in records and whatnot, but that's not free (as in you need to specify what/how), same goes for caching

mpenet10:07:39

I quite like transit nowadays, was super sceptical at first

jeroenvandijk10:07:51

I was thinking of making my own dictionary lookup for repeated items (for arbitrary objects) and have some kind of reference that needs to be replaced on a read. I have this working with transit, which is already quite compact. But Transit doesn't allow custom objects (forcing me to have hacky things like strings with underscore prefixes), so i was hoping Fressian could do this, but the basic respresentation is much bigger

jeroenvandijk10:07:12

Maybe i need to try harder 🙂

jeroenvandijk10:07:31

Transit is super good, but i have a special use case for a very compact representation

mpenet10:07:30

if you can trade some ressources for storage size, just compress the content 😛

mpenet10:07:49

quite a waste, but I don't know your constraints

jeroenvandijk10:07:00

hmm yeah actually if it runs on the jvm it can work. Maybe i should try traditional compression techniques instead indeed

jeroenvandijk10:07:36

ok one mystery for me though, why is the bytesize of (data->transit-bytes [1]) smaller than (data->transit-bytes 1) (i'm using the code in the gist above). It's 2 vs 6 bytes

jjttjj15:07:31

if I have a symbol/string representing a java method, is it possible to call that method on an object in a macro without using read-string or reflection?

jjttjj15:07:59

This is what I've attempted (assumes a constructor with no parameters, and setpairs returns a sequence of vectors of [<setter-symbol> <value to set>]

(defmacro m->obj [m class]
  (let [setters (set-pairs m class)]
    `(let [obj# (new ~class)]
       (doseq [[field# v#] ~setters]
         (doto obj# ((memfn field#) v#))
         ;;(doto obj# (. field# v#))
         )
       obj#)))

noisesmith17:07:48

I think that would work, as long as setter-symbol is actually a symbol (you can use the symbol function on a string, of course)

noisesmith17:07:11

that memfn will reflect when called at runtime btw

vlaaad15:07:00

it avoids reflection by type-hinting symbol holding an instance: (with-meta (gensym "instance") {:tag type-expr})

jjttjj15:07:36

ah! thanks, I think that'll be perfect

jjttjj15:07:45

type-expr would just basically be the class-name or int for primatives right?. Nevermind, got it!

Quest18:07:57

Build tooling power in 2019: deps.edn vs boot? I stopped following the evolution of Clojure build tooling in 2017. At the time, boot was the best choice from a standpoint of power / configurability, and I was quite happy switching to it over lein. Since then, I see that deps.edn and the clojure command line tool are being pushed as the new path forward. I found https://corfield.org/blog/2018/04/18/all-the-paths/ as a summation of the current "state of the world" from Apr 2018. While trying to understand how tools.deps is used to support dev flow for a backend microservice, I found https://github.com/clojure/tools.deps.alpha/wiki/Tools From observing the current state of tools.deps libraries, it seems that the recommendation for tools.deps.alpha is to use a Makefile and/or aliases to setup CLI executable "tasks" for your project (dev, uberjar, etc.) It seems that tools.deps doesn't provide a nice "Clojure build layer" similar to Boot, but leaves that up to other tools which may eventually wrap it. Unfortunately, those tools don't seem to exist, at least not at the power level of Boot. The best I can find is https://github.com/mbuczko/revolt My conclusion -- Boot is still going to be a lot easier to work with for a nice hot reloading clojure backend service. Many of boot's benefits seems achievable with tools.deps, but the path forward is not apparent and will require a lot of "roll your own" approach. Beyond the improvements in classpath handling, the benefits offered by tools.deps don't seem to get you significantly further than Boot.

Quest19:07:00

tl;dr: tools.deps is still evolving, use Boot if you want to get off the ground (relatively) fast with a good foundation for a microservice ^ My question to this community -- is this conclusion right, or is there a part of the picture I am missing here?

Alex Miller (Clojure team)19:07:12

I think that’s pretty right when you scope it to “hot reloading Clojure backend service”

Alex Miller (Clojure team)19:07:54

But I think there are a large number of projects that are satisfied without using Boot or lein at all

Quest19:07:17

That's fair. I notice tools.deps has made huge leaps WRT raw CLI apps & graalVM

Quest19:07:39

And I'm looking forward to using pure tools.deps someday, but my hunch now is that I'd get more heartache than help

Alex Miller (Clojure team)19:07:10

If you need it, feel free to ask in #tools-deps

👍 4
carkh19:07:32

you will have to make the leap at some point though, there's no stopping this train

Quest19:07:06

^ I also get that impression, and I do agree with the vision behind tools.deps 🙂

carkh19:07:36

i see many projects use a dual approach, use lein and tools.deps together

carkh19:07:45

i'm not familiar with boot though

Alex Miller (Clojure team)19:07:14

I use tools.deps and maven together too (which is pretty easy with -Spom)

Alex Miller (Clojure team)19:07:56

Although that’s mostly in contrib projects which have a long maven history

Quest19:07:08

Haha, maven gets much further out of my realm of know-how. I never got the "proper Java background" much of the community comes from. I do notice https://github.com/RickMoynihan/lein-tools-deps & https://github.com/seancorfield/boot-tools-deps as integrated approaches, though I'm not sure what I'd use 'em for in practice

carkh19:07:38

I see this as a safe transition path, use tools.deps for its own power and services, use lein/boot for what you need that doesn't exist yet in this ecosystem

carkh19:07:51

like your hot reloading

Quest19:07:18

Makes sense. I'm interested in picking up https://juxt.pro/edge/index.html just to get off the ground, but I just noticed... they already made the leap from boot to deps.edn Even found https://github.com/juxt/edge/blob/master/examples/tutorial.vent/deps.edn as a SPA example with figwheel reloading.

Quest19:07:07

I can probably run with this example & eventually pull in revolt for a Clojure build layer. Out of respect for the support & work going into tools.deps, I'll give this a shot and see if I can make it work. Thanks @carkh & @alexmiller for the input!

carkh19:07:29

good luck !

seancorfield19:07:02

@quest Happy to answer questions about workflow/tooling -- we started with lein at work back in 2011, switched completely to boot in late 2015, and switched completely to clj/`deps.edn` last year.

Quest19:07:58

Thank you for the offer, and I may take you up on this later, but sparingly. Knowledgeable help is a precious resource 🙂

seancorfield19:07:44

I've also switched most of my OSS projects over to deps.edn now (instead of lein or boot).

frozenlock19:07:10

Still rocking that good old lein... what made you change @seancorfield?

seancorfield19:07:03

This talks about why we switched from lein to boot https://corfield.org/blog/2016/01/29/rebooting-clojure/

clj 4
dmaiocchi20:07:26

Thx Sean. I didnt even know there was a lein or boot alternative

jjttjj20:07:38

I need to use a macro to call getter methods with no arguments on an object. The following works when passed a string but cannot be passed something that needs to be evaluated.

(defmacro getter [nm obj]
  (let [getter-expr (symbol (str "." nm))]
    `(~getter-expr ~obj)))

(getter "getMyValue" myObject) ;;works

(let [s "getMyValue"] (getter s  myObject)) ;;doesn't work
Any tips that might help me get the second way working?

noisesmith21:07:11

it can't be done without eval, as s is just the symbol s until runtime

noisesmith21:07:57

with eval, you'll probably want a lambda that receives s as an argument, as eval doesn't see locals

jjttjj21:07:49

gotcha, thank you!