Fork me on GitHub
#clojure
<
2017-07-29
>
hmaurer09:07:32

Hi! Is there a “test reporter” for plumatic schema (or another schema lib?)?

hmaurer09:07:47

usecase: I want to validate API responses in tests against a schema and have a nice test report if things fail

noisesmith15:07:55

@hmaurer what I do is call s/check on the schema and the object, and fail if the result isn't nil with the output of check in the failure

hmaurer15:07:33

@noisesmith how should I configure clojure.test to have a nice output for “expected” and “actual” on the report?

noisesmith15:07:35

@hmaurer test/is lets you put anything you want in the second arg's string

noisesmith15:07:52

@hmaurer umm that's kind of what clojure.test does when you run the tests

noisesmith15:07:02

are you talking about doing this outside unit tests?

hmaurer15:07:06

it only seems to work fell for some predicates

hmaurer15:07:09

no, running the tests

hmaurer15:07:39

for example, if the predicate is any arbitrary function it won’t report much

hmaurer15:07:52

I wrote a dumb, poor “schema” toy lib

hmaurer15:07:06

and getting a report such as:

expected: (v (get flat-target k))
  actual: false

hmaurer15:07:21

(v is a predicate function)

hmaurer15:07:37

I’ll try with the approach you suggest

hmaurer15:07:03

(that’s running it with test/is)

noisesmith15:07:17

Clojure 1.9.0-alpha15
+user=> (require '[clojure.test :as t] '[schema.core :as s])
nil
+user=> (t/is (nil? (s/check [Number] ["a"])) "expected a number")

FAIL in () (NO_SOURCE_FILE:2)
expected a number
expected: (nil? (s/check [Number] ["a"]))
  actual: (not (nil? [(not (instance? java.lang.Number "a"))]))
false

hmaurer15:07:25

Maybe I could call do-report manually instance

hmaurer15:07:51

seems to work, ok, nevermind

noisesmith15:07:27

if you test a predicate, all you get in the error is that the predicate didn't return the true/false you expected

noisesmith16:07:53

+user=> (defmacro handle [obj prop fun]
  `(.setValue ~(symbol (str (name obj) "/" (name prop))) (fi javafx.event.ActionEvent ~(symbol "event") ~fun)))
#'user/handle
+user=> (macroexpand '(handle a :onAction (fn [x] nil)))
(. a/onAction setValue (user/fi javafx.event.ActionEvent event (fn [x] nil)))
I don't know if it's cider, but something's misbehaving

zilti16:07:55

...ah, it didn't compile and didn't say anythign

noisesmith16:07:31

oh - right- macroexpand silently outputs a form that doesn't start with a known macro

noisesmith16:07:38

even if the thing in it isn't defined

zilti16:07:53

Back to the drawing board, I guess. Apparently I'm still too dumb to do macros.

noisesmith16:07:36

Cider magic introduces ambiguities and special cases about what's been evaluated and what hasn't, it's an easy mistake

noisesmith16:07:07

and macroexpand doesn't care if the things inside it are known, it just expands known macros and leaves the rest alone

zilti16:07:27

Ok so it expands with C-x e in the editor, but not in the REPL, which is set to be the same ns. Huh.

zilti16:07:57

Heh ^^ But I wouldn't want to miss it. It's weird, but it's great.

zilti16:07:12

...like Emacs, I guess 😛

luxbock16:07:09

C-x e by default is for calling a keyboard macro inside Emacs, and I think the default binding for expanding a clojure macro is C-c RET which works for me from the REPL as well

luxbock16:07:37

C-c RET for cider-macroexpand-1 and C-c M-m for cider-macroexpand-all

zilti16:07:15

Ah. So clojure.core/macroexpand isn't expected to work in the CIDER REPL?

luxbock16:07:00

oh right you are typing it in the REPL, let me try that

luxbock16:07:12

I get the same result as @noisesmith when typing it in the REPL

zilti16:07:06

It simply returns the input for me on the REPL, but works fine when evaluating with C-x e in the editor

zilti16:07:48

But C-c RET is a more-than-worthy REPLacement anyway

luxbock16:07:48

what is C-x e bound to for you?

zilti16:07:50

The default, that is evaluating the expression at the cursor

luxbock16:07:27

perhaps you mean C-x C-e?

zilti16:07:54

Ah, yes, right.

luxbock16:07:02

it works for me the same way regardless of whether I type it or evaluate the form in a code buffer or in the REPL

luxbock16:07:47

I'm not on the latest version however, so perhaps something changed, anyways this should probably belong to #cider

dpsutton16:07:10

fizzbuzz.core> (macroexpand-1 '(handle a :onAction (fn [x] nil)))
(.setValue a/onAction (fizzbuzz.core/fi javafx.event.ActionEvent event (fn [x] nil)))

dpsutton16:07:23

am i missing something from the discussion on macroexpansion with CIDER?

zilti16:07:41

@dpsutton no, you don't miss anything. `clojurefx.controllergen> (macroexpand-1 '(handle a :onAction (fn [x] nil))) (handle a :onAction (fn [x] nil)`

zilti16:07:29

Maybe I gotta restart cider. Maybe I've messed something up.

dpsutton16:07:50

what does (meta #'handle) return?

zilti16:07:45

Already restarted the REPL now... I guess what one'd expect now. {:arglists ([obj prop fun]), :line 37, :column 1, :file "/home/zilti/projects/clojurefx/src/clojurefx/clojurefx.clj", :name handle, :ns #namespace[clojurefx.clojurefx], :macro true} but it works now

hmaurer22:07:19

Would it make sense to use a record with no fields purely to implement a protocol?

hmaurer22:07:40

e.g. use the record as a way to get an object implementing some functions (the protocol) so you know it follows a certain interface

hmaurer22:07:46

without needing the record to hold any data

hmaurer22:07:44

It’s a bit of an odd question, I am not sure it’s sensible

noisesmith22:07:30

sounds like a job for deftype or reify

noisesmith22:07:54

deftype if you need a named class, reify if you don't

hmaurer22:07:52

I found a better solution to my problem (not involving this scheme) but my question still stands. To give you some context, I have a bunch of entities that have different access patterns. I was thinking of having an Entity protocol with some functions such as get, put, etc (`EntityRepository` might be a better name actually). Then have a record per entity type implementing that protocol, e.g. (defrecord UserRepository [] EntityRepository (get...

noisesmith22:07:03

the syntax to use reify or deftype there is nearly identical

hmaurer22:07:12

it wasn’t really meant as a way to allow to polymorphically pass any EntityRepository

hmaurer22:07:21

but more as a way to ensure that UserRepository implements the right methods

noisesmith22:07:39

and it will behave the same (except the whole "acts like a hash map" part)

hmaurer22:07:45

(to have a consistent API for accessing entities across the app, even if at the call point I always know what kind of entity I am accessing)

noisesmith22:07:07

I still don't see why any of these things imply a defrecord instead of deftype or reify

hmaurer22:07:21

I don’t either, I’ve no idea what deftype does 😄 looking now

hmaurer22:07:35

I was just trying to give you a bit more context, just in case

hmaurer22:07:36

yeah you are right, deftype is a better idea in this case

noisesmith22:07:14

it recommends reify for your case, unless you need a named type for some reason not mentioned yet

hmaurer22:07:15

defrecord is just sugar on top of deftype right?

hmaurer22:07:40

ah, thanks, helpful flow chart!

noisesmith22:07:49

it's like deftype but it automatically acts like a hash map (which is a feature you don't need)

hmaurer22:07:17

Could I implement that behaviour myself on top of deftype?

hmaurer22:07:29

by implementing some protocols?

hmaurer22:07:35

or it’s some compiler wizardry?

noisesmith22:07:54

deftype implements protocols, so yes you can do it by implementing the right ones

hmaurer22:07:54

(just curious, not planning on re-inventing defrecord)

hmaurer22:07:26

ok; thank you 🙂

mjo324_5622:07:50

what is a safe way to suppress ALL output from a called function? I tried both wrapping it up with "with-out-str" and redirecting output with bindings like "(binding [out "chuj" err "chuj2"] ", but my called function still outputs tons of shit

darwin22:07:49

@mjo324_56 the code could be using System/out directly

mjo324_5622:07:00

(binding [out "chuj" err "chuj2" System/out "chuj3"] ??

darwin22:07:24

System/out is a Java thing, imagine calling into native java code which does some printing

hmaurer22:07:31

@mjo324_56 edgy kids would run it in a Docker container for complete isolation kappa

darwin22:07:39

@hmaurer I don’t understand how Docker could be related to the problem, or I don’t understand the problem 🙂

hmaurer22:07:52

I was trolling, sorry

darwin22:07:05

he calls a function, which can potentially call some non-clojure code, which can potentially print something, redirecting clojure’s print facilities does not help redirecting all possible output

darwin22:07:42

even clojure code could do directly (.write System/out "something") bypassing Clojure’s *out*

darwin22:07:55

if this is what is happening, @mjo324_56 would have to capture the output on lower-level in Java land, something like this: https://stackoverflow.com/a/8708357/84283

mjo324_5622:07:40

I could run the function in a separate process and use OS stuff to redirect it's output, this is(besides inefficient and ugly) not so good since my function is changing some global variables

darwin23:07:08

@mjo324_56 the question is who is printing and via what mechanism, is it just clojure code? on the same thread? do you have control over it? I assume it is more complicated and you don’t know exactly.

mjo324_5623:07:43

my function runs in a seperate thread, so i think @darwin 's solution should not be good, since all of system.out is suppressed this way. Thread1 starts thread2 with the function that i want to silence

mjo324_5623:07:08

the lowlevel java solution, as far as i understand it, suppress all of system.out

mjo324_5623:07:22

so also that of thread1

darwin23:07:38

well, you might simply be doing bindings on a wrong thread

darwin23:07:02

binding is local to current thread

mjo324_5623:07:13

@darwin i am calling a third library function, that's printing the mess, havent looked deep inside it to know how it prints all the mess

mjo324_5623:07:07

clj-http.client is the evil doer

noisesmith23:07:16

it should just be a logger

noisesmith23:07:25

if you configure it's logger you can fix it

mjo324_5623:07:33

since it's http stuff i rather expect it to run on a couple of threads

mjo324_5623:07:27

@noisesmith , yes i could do it, but since i have function mappings and i plan to expand the code, I would prefer to have a generic method to suppress output

mjo324_5623:07:13

i can fix clj-http.client logging today but I will have the same problem when i add some other stuff tommorrow

noisesmith23:07:36

no, not fix the logging, configure it - that's the proper way to fix logging behavior, is to provide config

noisesmith23:07:28

there are only so many logging libs out there

noisesmith23:07:41

(too many, but not arbitrarily many)

noisesmith23:07:56

anything in the clj ecosystem should be configurable via slf4j

mjo324_5623:07:03

and the logging libraries are able to redirect output ?

mjo324_5623:07:09

in a generic way?

noisesmith23:07:09

that's what they are for, yes

zilti23:07:51

taoensso.timbre is also able to configure slf4j in a very simple way

noisesmith23:07:42

yeah, I use timbre's slf4j extension to control java logging things in my app

noisesmith23:07:14

it's annoying, then you get it configured and pretty much never have to touch it again

mjo324_5623:07:24

none of themlooks simple i need stuff like (shut-up (myfunction..

noisesmith23:07:01

you set the log level to a less verbose level for the specific package doing the printing

mjo324_5623:07:17

log-capture!

noisesmith23:07:19

then you just call stuff and if it is from that package the verbosity is lower

mjo324_5623:07:06

i don't like these solutions, people on the web are complaining that the loggers themself spit output

mjo324_5623:07:35

you see, i don't need a logger, want to silence a function

noisesmith23:07:44

the function is using a logger

noisesmith23:07:52

the way to make a logger shut up is to configure it properly

mjo324_5623:07:58

so putting a logging library into the project is bloating my software

noisesmith23:07:07

OK, have fun

noisesmith23:07:33

the logging library - the big one - is already pulled in by clj-http

noisesmith23:07:54

you can fix this with a small text file that tells the logger how to behave - or some other crazy hack if you really prefer that

noisesmith23:07:36

the timbre solution is better if you are using timbre / need timbre's features, but the logger that clj-http is already pulling can be controlled with a small props file or xml document

darwin23:07:19

a friendly alternative: I’m not friends with xml, I tend to use this: https://github.com/malcolmsparks/clj-logging-config

mjo324_5623:07:11

@noisesmith clj-http uses this logging library, ok i can configure it, i use many different functions calls, maybe one of em uses another logging library which overrides this stuff?! That's why i'd prefer a "hack", a function which simply silences ALL output of a called function

noisesmith23:07:33

there's too many ways to output for that to work

noisesmith23:07:39

one of those ways is via a logging library

noisesmith23:07:46

they run their own thread for logging, often

noisesmith23:07:58

so you can't intervene except on the level of that lib and it's thread

mjo324_5623:07:15

i also don;t see in the source of which logging library where is used, neither do i understand how to do this, i put a log4j.properties file into the root of the project, thats supposed to turn logging off

mjo324_5623:07:00

that's why i'd prefer a hack to silence it completeley, simply to much shit is there to configure which both takes my time and makes the projhect more complex

noisesmith23:07:26

I've already given my opinion on all this, my experience with the jvm tells me that a general supression of output is more complex and difficult to implement than the configs to control the logging that your libraries pull in. But it's your project, do it how you like. The ecosystem is not going to make this easy for you.