Fork me on GitHub
#beginners
<
2021-09-30
>
Aditya Krishna12:09:58

(ns cheshire-cat.handler
  (:require [compojure.core :refer [defroutes GET]]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]
            [ring.middleware.json :refer [wrap-json-response]]
            [ring.util.response :refer [response]]))

(defroutes app-routes
  (GET "/" [] "Hello World")
  (GET "/cheshire-cat" [] (response {:test 123 :pass true}))
  (route/not-found "Not Found")
  )

(def app
  (-> app-routes
      (wrap-defaults site-defaults)
      (wrap-json-response)))

Johan Thorén14:09:21

Just a tip, it's easier to read the code if you use the function code block instead of code.

👍 1
Aditya Krishna12:09:11

This doesn't add the content-type headers

Aditya Krishna12:09:23

I'm following along a example from Living Clojure

Aditya Krishna12:09:36

Am I missing anything

Apple13:09:54

Which func is supposed to do that - adding content-type? ring.util.response/response?

Aditya Krishna13:09:18

ring.middleware.json/wrap-json-response

Aditya Krishna13:09:22

(defn wrap-json-response
 "Middleware that converts responses with a map or a vector for a body into a
 JSON response. Accepts the following options:
  :pretty      - when true, pretty-print the JSON
  :escape-non-ascii - when true, non-ASCII characters are escaped with \\u"
 [handler & [{:as options}]]
 (fn [request]
  (let [response (handler request)]
   (if (coll? (:body response))
    (let [json-response (update-in response [:body] json/generate-string options)]
     (if (contains? (:headers response) "Content-Type")
      json-response
      (content-type json-response "application/json; charset=utf-8")))
    response))))

Apple13:09:39

wrap-json-response is last in the flow to process. perhaps some other middleware added content-type earlier.

Apple13:09:58

if you take wrap-json-response out what do you see in the response?

Fredrik14:09:50

The (wrap-defaults site-defaults) includes the wrap-content-type middleware, which bases off either the content-type header or the file extension of the requested resource. Otherwise, it defaults to "application/octet-stream".

Fredrik14:09:19

If you give wrap-defaults the argument (assoc-in site-defaults [:responses :content-types] false) , you'll let wrap-json-response have a chance of setting the content type instead.

Apple14:09:23

or just move wrap-json-response up the chain. like

(def app
  (-> app-routes
      (wrap-json-response)
      (wrap-defaults site-defaults)))

Apple14:09:37

which page of 'Living Clojure' was it?

Fredrik14:09:50

I think I found the book's example around page 150. There the code looks just like @zengxh’s suggestion, which is easier than mine, you can play around with the defaults later if you want.

Aditya Krishna19:09:42

Yes thank you. @zengxh was right. I missed the order of function evaluation.

Sam Ferrell17:09:35

Is comparing two pure Clojure values with = O(1)?

emccue17:09:28

it short circuits on identical? values but (= [1 2 3] (conj [1 2] 3)) still needs to do 3 units of work

Sam Ferrell17:09:18

So I'm mistaken thinking Clojure creates a hash for those persistent data structures automatically, which could be used for comparison?

alexmiller17:09:58

Hashes are used for fast fails, but can't be used for fast validation as they can collide

Sam Ferrell17:09:13

That makes sense

Sam Ferrell17:09:28

So if my use-case could tolerate collisions I could compare hashes instead? Is (hash x) constant?

noisesmith17:09:03

hash is cached for each collection, but can't be constant time - needs to be linear for the size on collections

Sam Ferrell17:09:57

Right, got it. Thanks for your help!

eyalch19:09:46

How are you guys passing environment variables for development? I know there's https://github.com/weavejester/environ, but is there anything simpler?

Andrés Rodríguez19:09:51

We're using https://github.com/yogthos/config in the project I'm working on, but it's essentially the same concept and the standard MO for development envs across all languages that I know of: A .gitignored file somewhere that gets loaded right before start, in lieu of an environment (like Kubernetes, systemd, Elastic Beanstalk, etc...) loading the right envs from a secret store.

Kelvin19:09:34

For our latest project we used Juxt's aero library, which is similar to yogthos/config except it's in various ways more flexible. e.g. you can have different "profiles" for test config, prod config, etc, reduce duplication via map merging, and so on. https://github.com/juxt/aero

Andrés Rodríguez19:09:10

@U02CMB8L0LC It ships with edn support OOTB. I for one don't care too much about that, but if you like that, either that or Kelvin's suggestion should do well!

eyalch19:09:59

What if I want to define the structure of my configuration and, for example, have some values required, e.g. the DB URI?

noisesmith19:09:18

aero lets you pull in vals into your structure from the environment, or java system properties (set on java command line)

Kelvin19:09:42

https://clojurians.slack.com/archives/C053AK3F9/p1633031159169800?thread_ts=1633029826.168200&amp;cid=C053AK3F9 We actually used clojure spec to validate the configuration map after it's read in. It's pretty straightforward to define a map spec using s/keys and then using that spec to validate.

Kelvin20:09:13

Never heard of it so can't say much beyond "it looks interesting" - I'm sure someone here has enough experience to intelligently compare it to using aero + spec or other ways of doing config.

didibus20:09:52

If you want simple, just setup environment variables in a shell and launch your repl with it. Then you can use (System/getenv) to fetch them. Or if using Lein, this plugin setups hooks that does it for you: https://github.com/athos/lein-with-env-vars

didibus20:09:59

But I wouldn't recommend using environment variables as configuration in your app, so I'm assuming this is because you use a library/framework that itself makes use of an environment variable?

noisesmith20:09:35

to add to @U0K064KQV’s suggestion of setting args at the cli:

(ins)[email protected]:~$ cat env-file
FOO=1
BAR=2
(ins)[email protected]:~$ env $(<env-file) clj
Clojure 1.10.1
(ins)user=> (map #(System/getenv %) ["FOO" "BAR"])
("1" "2")

noisesmith20:09:44

(that is to say, you can source a file with one env setting per line via the env command)

noisesmith20:09:14

@U0K064KQV I think a lot of people get creds from the env because they follow a 12-factor pattern - what's your alternate suggestion, a credential server?

didibus20:09:51

For creds, ya, I'd use a keystore like AWS KMS

didibus20:09:24

It pushes creds to your box in a file, and you look it up from there. The benefit is rotation and all that, more secure then env as well.

didibus20:09:21

Or I mean, whatever sets up an environment var in your prod hosts could maybe instead create a file with the creds in them and with permissions to the user that your process runs under. And then in your application you look up the creds from that file. And in dev, what you have is config to the path to the file. So your dev config can have a path to such file that is somewhere else with your dev credential.

didibus20:09:34

That way, the only thing you use the environment for is for defining the environment you are under. So something like (getconfig (System/genenv "environement")) and either your config is EDN like this: {<environment> {}} where <evironement> is the value of the ennvironment env key like: PROD-NA, DEV, etc. Or maybe you have different config files like prod-na.edn and dev.edn and the value of the env environment key looks up the correct file for the config, etc. And dev can just be the default, so you don't even need to set environment for dev work.

didibus21:09:34

And you can also easily add yourself an inheritance chain if you think that's useful by having a scheme for your environment like: A:B:C which is a bit like how aliases work in the clojure CLI, this would say that the config should be (merge (getconfig A) (getconfig B) (getconfig C))

didibus21:09:44

So you can do something like: default:north-america:usa which would like load up the defaults, then override them with the north-america specific config, and finally with the usa specific config. So then on your USA fleet, you can set the environment var ENVIRONMENT="default:prod-na:usa" That way all your config is in code and will be committed alongside in git. Which is great for having code reviews over the config as well as the config rollbacks automatically with your deployments and code, and a full config history trail too.

Usman20:09:31

Hi guys - I am having some trouble with a scenario. I have a vector of ids, let’s call it ids: ["1" "2" "3"]. I want to map over the array and call 4 different functions and then check if any of the functions returned true. I have added a snipped of my code below to hopefully display what I’m trying to do.

(some
          #(true?
             [(is-campaign-conflict? %)
              (is-provider-conflict? %)
              (is-product-conflict? %)
              (is-link-conflict? %)])
          ids)

Bob B21:09:29

depending on context (I'm making a bunch of assumptions around your question), a hierarchy might be a good fit as well:

(let [conflict-hier (-> (make-hierarchy)
                      (derive :conflicts/campaign :conflicts/special)
                      (derive :conflicts/provider :conflicts/special))
      ids [{:type :conflicts/something-else}
           {:type :conflicts/provider}]]
  (some #(isa? conflict-hier (:type %) :conflicts/special) ids))
=> true

bhenry00:10:36

if you only need to know if any of the ids trigger any of the functions you can do this:

user=> (mapcat (juxt pos? neg? zero?) [0 1 2 3])
(false false true true false false true false false true false false)
but if you need to know which ids caused the trues then you can do some more logic on this:
user=> (map (juxt pos? neg? zero?) [0 1 2 3])
([false false true] [true false false] [true false false] [true false false])

Ed19:10:46

some-fn will let you combine some predicates into a single function that will return true if one of the predicates returns true ... Does that sound useful?

Ed19:10:24

Oop ... Just noticed you've already been pointed in that direction facepalm

Sam Ferrell20:09:17

Maybe you should define a new function that takes the id and returns true or false...

(defn foo? [id]
  (or (is-campaign-conflict? id)
      (is-provider-conflict? id)
      (is-product-conflict? id)
      (is-link-conflict? id)))

Sam Ferrell20:09:44

Then you can call (some foo? ids)

dpsutton21:09:55

check out (doc some-fn)