Fork me on GitHub
#beginners
<
2017-10-27
>
Drew Verlee02:10:55

How do i represent a jodaTime object in my code for a test? If i just put the #object... i get an error about a tag literal reader.

Drew Verlee02:10:08

put another way, if i eval the code and use the output in my test then i get an error. #object[org.joda.time.DateTime 0x2b69f59 "0085-04-07T00:00:00.000Z"] isn't something i can read back into the repl.

shaun-mahood02:10:49

@drewverlee Are you wanting to use joda time directly or are you using clj-time?

Drew Verlee02:10:59

I'm just calling functions in the clj-time namespace. i haven't had to do more then that to get the functionality i want.

shaun-mahood02:10:27

That's all I've ever needed too - I'm away from the computer, but I think you can use the isequal method and compare your object against another that your test creates. Is that along the lines of what you wanted?

Drew Verlee02:10:41

oh, yea. that makes perfect sense

Drew Verlee02:10:26

This is what i get for trying to work while half asleep.

shaun-mahood02:10:27

If you're using the before and after comparisons, one is inclusive and one is exclusive - it totally messes me up if I forget. Good to see you on here too, it was fun hanging out at he conj!

seancorfield02:10:09

@drewverlee I'm one of the clj-time maintainers so let me know if there are specific questions I can help with...

seancorfield02:10:09

(at work we're in the process of migrating to clojure.java-time which is a nice, thin wrapper around Java Time introduced in Java 8)

juanjo.lenero03:10:54

What’s the most straight forward way to print whatever is coming in or out of a function? or in between forms?

juanjo.lenero03:10:16

Say I just want to know whats passing in between somewhere as quickly and with as less modifications to de code as possible

juanjo.lenero03:10:16

I.e. What’s the output of edn/read-string in the following snippet?

(def app-schema
  (-> "src/get_it_right/schema.edn"
      slurp
      edn/read-string
      (attach-resolvers {:get-product get-product
                         :get-products get-products
                         :get-product-price-history get-product-price-history
                         :scrape-products scrape-products
                         :signin signin
                         :signup signup
                         :signout signout
                         :request-password-reset request-password-reset
                         :reset-password reset-password})
      schema/compile))

juanjo.lenero03:10:43

But also whathever the one function returns here:

(defn signup
  [_ctx {:keys [email password]} _val]
  (one pool (user/create-user
             {:email email
              :password password})))

juanjo.lenero03:10:05

For the first one I guess I could do:

slurp
edn/read-string
(doto prn)
....
But what if I want to print the value along with a label to stdout? like "look here: " val

juanjo.lenero03:10:29

(doto (#(prn "here: " %))) is no longer as succint

juanjo.lenero03:10:07

Is there anything closer to elixir’s inspect (https://hexdocs.pm/elixir/IO.html#inspect/2) in the standard library?

juanjo.lenero03:10:42

I.e. Take a value, print it to stdout with an optional label and return that same value unchanged?

juanjo.lenero03:10:14

Something like:

slurp
edn/read-string
( inspect "here:")  -- Takes the output of edn/read-string, prints it to stdout after "here: " and passes it along.

juanjo.lenero05:10:37

I just went through the trouble of setting up sayid out of frustration

juanjo.lenero05:10:33

VERY impressed, best debugging experience I’ve had in my life.

mseddon12:10:20

is there a way I can extract source information from data passed into a macro and use it to provide helpful error messages pointing at the offending expression?

noisesmith17:10:00

if the solution is a macro, you need the compiler for it

noisesmith17:10:38

maybe I’m misreading that

tdantas17:10:58

hey again guys ! finally I’ve setup my application to use components ( thanks @noisesmith ) removed the lein-ring and runnin gmy application from REPL (go) after I call (go) my components boots up and the web-server as well ( web-server is one compoent ) and blocks my REPL ( waiting for request) how you guys normally does ? create a new system without web-server for the REPL ?

noisesmith17:10:35

typically I’d put the web server process in a future, and attach that future to the component

noisesmith17:10:55

@oliv depending on which server you are using, there is likely an optional argument to run in the background

noisesmith17:10:30

but more generally, if a component needs something to continue running, attaching a future to the component (then making sure it exits and cleans up when stopped) is the thing to do

tdantas17:10:40

sounds interesting the future approach ( pretty sure I have to study a little more to be able execute with future )

tdantas17:10:48

do you have any example for that ?

tdantas17:10:57

btw: I’m using jetty

tdantas17:10:30

and how is your workflow, every time you change the source code you (stop) / (start) the components again ?

noisesmith17:10:53

future is pretty easy - change (f) to (future (f)) - the biggest gotcha is that if it throws, you don’t see the error until something tries to dereference the future

noisesmith17:10:41

run-jetty definitely has an option for running in the background (run-jetty ... :join? false) https://ring-clojure.github.io/ring/ring.adapter.jetty.html

noisesmith17:10:12

some code doesn’t require a stop/start but that cycle should be short enough that it doesn’t matter much

tdantas18:10:35

Cool, so regarding the star / stop cycle, supposing I'm changing some function on my handler , eventually I will want to restart the server to test my fix

tdantas18:10:40

Do you do it manually or something else is listening for changes and restart the components to you ?

noisesmith18:10:22

either can happen, but there’s nothing doing that automatically by default - I restart by hand after reloading my code

tdantas18:10:10

Yeah, same here. Yeah definitely I will run my jetty on background

noisesmith18:10:34

also, a common trick is to pass the var for your handler to the server process, rather than the handler function itself - this ensures that the var is freshly looked up and dereferenced each time it is called. But this is incompatible with clojure.tools.namespace/refresh which deletes vars from namespaces and makes new ones

tdantas18:10:35

Releasing my repl , thank you for your time , let me know if you have any place for donation :)

tdantas18:10:26

I'm using the refresh , I confess I did not understand very well the var for your handler ...

tdantas18:10:40

Hopefully I will soon understand what you said

noisesmith18:10:33

#'handler is the var that contains handler

noisesmith18:10:41

if you call it, it calls handler after looking it up

noisesmith18:10:01

if you call (run-jetty handler …) it looks up handler once, and then jetty just uses that

noisesmith18:10:27

if you call (run-jetty #’handler …) it uses the var, which means it looks up the current value every time, which means it sees the new definition if you redefine it

noisesmith18:10:04

@oliv perhaps this helps

(ins)user=> (defn do-foo [] :old-value)
#'user/do-foo
(ins)user=> (defn capture [f] (fn [] (f)))
#'user/capture
(ins)user=> (def f1 (capture do-foo))
#'user/f1
(ins)user=> (def f2 (capture #'do-foo))
#'user/f2
(ins)user=> (f1)
:old-value
(ins)user=> (f2)
:old-value
(ins)user=> (defn do-foo [] :new-value)
#'user/do-foo
(ins)user=> (f1)
:old-value
(ins)user=> (f2)
:new-value

noisesmith18:10:46

the difference between capturing do-foo vs. #'do-foo

macgyver18:10:14

Hello all, I am trying to get Leiningen set up on Fedora and I am having issues. Have been following the install instructions and they don't seem to be working for me. Could someone please help me? If Leiningen was on rpm this would be much easier as well, but it doesn't seem to be.

manutter5118:10:22

What’s the problem?

macgyver18:10:17

I am having issues running the script. Do I save the script with lein.sh, or simply lein?

noisesmith18:10:43

lein is the shell script - it installs everything else

manutter5118:10:43

Easiest if you save the script as lein

manutter5118:10:09

I usually save mine to ~/bin/lein

noisesmith18:10:11

also you need to set it to be executable

macgyver18:10:13

OK, so my script is saved in /bin/lein

noisesmith18:10:35

that’s a weird place to put it - /bin is typically for system level stuff

manutter5118:10:42

Ok, you might have some permissions issues if you put it there.

manutter5118:10:12

It’s better in ~/bin, then you don’t have to use sudo for updates and such

dpsutton18:10:17

https://leiningen.org/ on their page they suggest a place like ~/bin which is drastically different from /bin

noisesmith18:10:25

most idiomatic on a *nix system would be to put it in /usr/local/bin or an individual user’s ~/bin/

manutter5118:10:29

but of course you have to have ~/bin in your PATH

macgyver18:10:45

ah, @dpsutton I didn't realize those 2 are different

noisesmith18:10:14

sadly on OSX there are system level tools that mess with /usr/local/bin which is totally wrong - but no such problem on fedora

manutter5118:10:12

@adamfranzen Yes, ~/bin means you make a bin directory in your home dir.

manutter5118:10:17

and there’s also some setup you need to do to make sure your $PATH contains ~/bin

dpsutton18:10:25

and then in ~/.profiles you can PATH="$HOME/bin:$PATH

macgyver18:10:25

ok, so I am emberassed to say, but I cannot access /bin. How do I access?

noisesmith18:10:36

it belongs to root

noisesmith18:10:49

only the package manager should be putting things there or removing things from it

dpsutton18:10:51

that's why it's not a great place to put something there. it's very near and dear to your system

macgyver18:10:52

as in I cannot move mv ~/Downloads/lein /bin

noisesmith18:10:05

right, you shouldn’t be doing that

manutter5118:10:07

That’s good because you don’t want to do that

manutter5118:10:21

You need the tilde in front of the /bin

manutter5118:10:35

mv ~/Downloads/lein ~/bin

dpsutton18:10:42

Adam, let's talk commands over before we run them. we'll get you set up 🙂

noisesmith18:10:46

~/bin is a shell syntax that expands to $HOME/bin/

dpsutton18:10:42

that may have sounded condescending and i really don't mean it that way. but feel free to ask about a command before you run it if you like

macgyver18:10:48

☝️ no sweat, I appreciate the help. I am new to UNIX thanks for the help

Empperi18:10:59

Unix directory structure is kinda confusing, so no worries

Empperi18:10:37

And it is a bit different between different distros to make it even more confusing

macgyver18:10:50

~bin mv ~/Downloads/lein ~/bin
mv: cannot stat '/home/afran/Downloads/lein': No such file or directory

manutter5118:10:35

Oh wait, it can’t find your downloaded file

Empperi18:10:55

Just wget it? :)

noisesmith18:10:14

it probably downloaded as lein.sh or leiningen or something

noisesmith18:10:21

you can use ls to see what is in ~/Downloads

dpsutton18:10:20

we are all on pins and needles here haha

macgyver18:10:21

Sorry to leave you hanging guys. I got it figured out. Details to follow...

macgyver18:10:35

A coworker came in and distracted me ha

rcustodio19:10:26

Hi, I’m using https://github.com/ring-clojure/ring, the request headers are lowercase, but that’s okey, but seems like the response are lowercase as well, is there anyway to change that? a middleware or something?

noisesmith19:10:32

I’ve used ring for years and this has never been a problem - what specifically is going wrong?

rcustodio19:10:25

Clients who check headers in a case-sensitive way

noisesmith19:10:20

if nothing else you can explicitly return a hash-map {:headers {.. ...} :status ... :body ...}

noisesmith19:10:51

it should merge any headers under that key into the response

rcustodio19:10:51

I do that

(defn- handler [request]
  (let [response (rfour/request (slurp (:body request))
                                (settings [:rabbitmq :exchange :topic])
                                (path->rk (request :uri))
                                :headers (merge {"w-path" (path-rstrip (request :uri))
                                                 "w-method" (string/upper-case (name (request :request-method)))
                                                 "w-query" (request :query-string)
                                                 "w-segments-length" (-> (request :uri) (path-strip) (path-segments) (count))}
                                                (-> (request :uri) (path-strip) (path-segments) (request-segments))
                                                (-> (request :headers) (request-headers))))]
    (println (response-headers (@response :headers)))
    {:status (response-status request @response)
     :headers (response-headers (@response :headers))
     :body (apply str (map char (@response :body)))}))
The print
{Content-Type application/json}
The response at postman
content-length →76
content-type →application/json
date →Fri, 27 Oct 2017 19:04:53 GMT
server →Jetty(9.2.21.v20170120)

rcustodio19:10:34

There are some clientes (at least in Brazil) who is very boring about those kind of things

dpsutton19:10:55

they are expecting Content-Type?

dpsutton19:10:18

> Each header field consists of a case-insensitive field name followed > by a colon (":"), optional leading whitespace, the field value, and > optional trailing whitespace.

dpsutton19:10:44

well nice to know that actual answer, even if it doesn't help

rcustodio19:10:25

Yeah, they go against some things… sad but true

tdantas20:10:18

#'handler` is the var that contains `handler`
if you call it, it calls handler after looking it up
if you call (run-jetty handler …) it looks up handler once, and then jetty just uses that
if you call (run-jetty #'handler …) it uses the var, which means it looks up the current value every time, which means it sees the new definition if you redefine it
@noisesmith thank you again , mind blowing !!! I thought handler was the symbol which points to the Var #'handler which points to the value ( the function )

noisesmith20:10:59

right - but the question is when (how often) that lookup is done

noisesmith20:10:22

handler is looked up once (if provided as an arument) or every time (if compiled into a form)

noisesmith20:10:36

#’handler is looked up every time, period

tdantas20:10:00

yeah ! makes completly sense , please put your crypto wallet public sir

tdantas20:10:40

the handler symbol will be evaluated to the value ( symbol — var — value )

tdantas21:10:26

one requirement of my system is to be able to trace the request among multiple requests. to accomplish that I’m tracing all the requests that comes in, every endpoints is checking if there is something in the header X-Tracing-Id , otherwise must create one. All the logs into my system must be logged with the traceId on it. to do that tracing part, I’m creating one middleware (defn tracing [handler] … ) that will be responsible for get or create the tracing id. after that I’m assoc into the request.

(defn tracing [{:keys [headers]}]
  (update-in headers [:x-tracing-id] 
             (fnil identity (.toString (java.util.UUID/randomUUID)))))

(defn mw-tracing [handler] 
   (fn [request] 
   (handler (tracing request))) 
that is fine, works great ! my request will be assoced with the tracing-id or a new one. what really bother me is the coupling that I’m building. the request map is containing everything I need, and all my functions are receiving that huge map right now I have * my components ( :db, :aws-clients, :rabbit-mq-client, :redis-client ) ( should the log be a component ? ) * my tracing id * logged user ( if is a logged user or anonymous ) and the list will grow and grow My service layer - or handler layer is receiving the request and destructuring what needs , is the correct way to build clojure systems ? how you guys are building your systems ?

seancorfield21:10:18

Remember that Clojure favors open data -- so having keys in a map that a particular function ignores is fine. Each function can just destructure the keys it cares about out of the incoming map. If it calls downstream functions that need the map, pass the whole map (so lower-level dependencies can extract whatever they need from the map without intermediate functions caring about those things).

seancorfield21:10:26

Since the map is immutable and persistent, it's not like you're copying it as you pass it around so there's no performance or memory overhead by having it contain more things (beyond the once-per-request needing to add those things into the request).

tdantas21:10:08

I see ! @seancorfield, regarding the log + tracing, how would you handle that ? my logging is a singleton ( clojure.tools.logging ), should I downstream to all my functions the x-tracing-id and use it on my (log/info …) calls ?

phronmophobic22:10:18

I might consider having your tracing middleware create a new key for the tracing id rather than inserting one into the headers map

phronmophobic22:10:26

maybe something like

(defn tracing [{:keys [headers] :as req}]
  (assoc req
         :tracing-id (if (contains? headers :x-tracing-id)
                       (:x-tracing-id headers)
                       (.toString (java.util.UUID/randomUUID)))))

phronmophobic22:10:08

the reasons being 1) middleware or request handlers that want to generically do something with headers will now have a header field that wasn’t part of the actual request

noisesmith22:10:14

(headers :x-tracing-id (UUID/randomUUID) can replace that if

phronmophobic22:10:45

2) if at some point you want to figure out tracing ids some other way, you can do that transparently

phronmophobic22:10:06

for eg, you want to use headers when someone is logged in, but maybe use GET parameters for testing or something

phronmophobic22:10:49

although, depending on how much your system grows, it may not actually ever matter

phronmophobic22:10:07

@noisesmith, would that create a UUID, regardless of whether you need it?

noisesmith22:10:24

yes - but that’s relatively cheap

tdantas22:10:18

yeah, thanks for the inputs !

phronmophobic22:10:21

also, using namespaced keywords sounds like a neat idea, but I haven’t tried it

noisesmith22:10:21

about a 10th of an ms

tdantas22:10:35

so, we have the request assoced with :x-tracing-id , all my functions ( business logic ) must receive somehow the x-tracing-id because we must logged it. is that the way you build your system ?

noisesmith22:10:22

if you have an id but it might be brand new, it might make sense to both have the effective id (there whether it’s new or not) and a boolean indicating whether the request had one coming in

juanjo.lenero22:10:06

Is it considered good practice to catch an exception in a low-level namespace (e.g. Catch unique constraint exception in db namespace) and rethrow it as a custom domain exception (e.g. username-already-taken) that your route handlers can understand and present to the user?

tdantas22:10:11

exception/failure is always a recurrent problem to me, no matter language I’m working on. what I like to do is capture the exception on lower level and always returns one “object” that represents the operation Etiher success or Failure. The caller must check if the operation performed properly or not

juanjo.lenero22:10:37

I’m using dire: https://github.com/MichaelDrogalis/dire to avoid having try-catch blocks all over and also avoid having to check for success/failure of multiple operations

juanjo.lenero22:10:26

So I would register a handler for my db query function that would catch the PGSQLException and retrow it as a custom exception

juanjo.lenero22:10:54

my route-handler would have another handler that catches the custom exception and returns a useful error-map to the client

seancorfield22:10:37

Re: creating the UUID each time, there's always (or (headers "x-tracing-id") (java.util.UUID/randomUUID)) -- and if I'm not mistaken, aren't Ring headers keyed as strings, not keywords?

noisesmith22:10:26

@oliv the problem with this is there is no general way to prevent all throws, so introducing something other than try/catch means you have two systems to deal with

noisesmith22:10:00

the only way to have one consistent system for handling failures is to use try/catch and throw

seancorfield22:10:22

Where you have "expected exceptions" (oh, that there is such a thing!), then it's reasonable to catch them locally and turn them into something else -- but if they're expected then that should flow into regular error handling that you expect to provide to your end users.

seancorfield22:10:19

Where you have "unexpected exceptions" (where they are truly exceptional) then letting them propagate and logging them at the top level with a "Sorry, an unexpected problem occurred with your request!" end user message is reasonable.

seancorfield22:10:09

With databases, we've gotten used to relying on constraint violations throwing exceptions when those are often expected failures, not truly exceptions.

seancorfield22:10:58

It's like throwing an exception on end of file -- of course there's going to be an end of file: that's not exceptional! Hitting EOF before reading a complete/valid form... OK, that might be considered exceptional depending on who controls the file format. (user-supplied, definitely should be expected; system-generated, yeah, throw an exception).

tdantas22:10:04

@noisesmith you are right. for that cases I have a global middleware to handle uncaught exceptions. so how you do then ? where are you handling the exceptions ? you have specific handlers for your exceptions ?

noisesmith22:10:26

my experience with co-workers trying to provide some other systems for handling exceptional but recoverable conditions is that we end up with a multiplicity of success / failure tracking subsystems and the diversity makes it easy to mishandle them, with problems including failing to abort on error states (resulting in garbage or data corruption), hiding of failures so that only downstream symptoms are visible (which makes debugging harder)

seancorfield22:10:46

Yes! ☝️:skin-tone-2:

seancorfield22:10:28

The two places to catch an exception are: 1) right at the source if you can do something useful about it 2) right at the top level where you can log it and apologize.

seancorfield22:10:52

(note: the "source" is defined by module or system boundaries in reality so it moves around a bit)

noisesmith22:10:55

I’ve literally seen: a) let’s handle this error state by logging an error then moving on b) I’m tired of all this noise in the logs I’m removing the error logging clause c) something is wrong we have no idea what there are no symptoms other than the task failing or returning garbage - when you see the sequence all in one place it’s comical, but as an interaction between code authors it’s an easy state to end up in

noisesmith22:10:26

also, on some level the correlation between misbehaviors and exception handling is kind of like the correlation between hospitals and mortality - it’s the nature of the thing that a lot of things going wrong are going to interact with your exception handling system, this doesn’t mean the exception handling is at fault

juanjo.lenero22:10:45

So I should catch the constraint exceptions that are related to my buisness logic at/near-to my query executor function (i.e. duplicate email/username) and let other exceptions unexpected exeptions bubble up to be logged?

seancorfield23:10:22

That is probably closest to the approach I generally take, yes, @juanjo.lenero