Fork me on GitHub
#beginners
<
2021-09-17
>
Chase01:09:58

What are you folks using to handle environment variables, specifically in a project using deps.edn ?

Chase01:09:35

I was using https://github.com/weavejester/environ and also found https://github.com/yogthos/config but they are both geared around lein and using your project.clj file or .lein-env files.

Chase01:09:05

I would like to have a .env file myself but am also scared about doing something stupid, security wise.

Chase01:09:33

Like where do I put my postgres db password that I am accessing with jdbc?

dorab01:09:56

I'm pretty sure both should work with deps.edn. have you tried them and run across a problem?

Chase01:09:04

As a side note, I have a local postgres db right for development, but I'm also setting up a db through heroku to use on the same project in production. I'm overwhelmed trying to think of how I keep such things in sync. Are they going to have different db urls and such, right? Where am I storing all these environmental variables?

Chase01:09:42

Well, for example, the environ entire readme is describing how to set up your profile.clj to use it and access your .lein-env file. I have no clue how to translate all that into deps.edn workflow

Chase01:09:40

The example it is using talks about a dev and prod db access which sounds like exactly what I am going to need.

dorab01:09:45

What OS are you on locally? What IDE are you using?

Chase01:09:12

debian OS with nvim

dorab01:09:59

OK. do you know how to set an environment variable on Debian?

Chase01:09:33

Should I just set global environment variables for such things? I've done that for api access things in the past and it worked out fine. Accessing it with (System/getenv "my_variable")

dorab01:09:51

That is exactly correct.

dorab01:09:00

You can use environ and config, but you can also use what you suggest above directly, without using those libraries.

dorab01:09:38

I don't know anything about Heroku, but I presume there is some way of setting the global environment variables as well.

dorab01:09:28

And the settings of the dev and prod variables will probably need to be different - obviously.

Chase01:09:58

Ok cool, I'll work on using these directly without the libraries.

dorab01:09:54

Some sort of configuration management library will be useful as you work on more complicated projects, but for your current purposes, direct is probably best. FWIW, I tend to prefer the juxt/aero library for config management.

Chase01:09:27

Ok, cool, I'll take a look at that too. Thanks!

sova-soars-the-sora03:09:43

So at work my co-workers came upon a solution: have a configuration.edn in the project root with default (non-secret) values

sova-soars-the-sora03:09:06

Then have a file in your home directory or someplace outside the purview of version control that overrides them if it exists.

sova-soars-the-sora03:09:26

So you can never accidentally commit your secrets to forever-version-control

sova-soars-the-sora03:09:45

and it kinda dummy-proofs the idea of forgetting to update your .gitignore

dorab03:09:15

juxt/aero uses a similar approach. The secrets are stored in a file in the home dir and are included into the project config file using a reader macro.

dharrigan06:09:04

Yes, I use aero too. It's great. Really really good library.

Fredrik10:09:45

Vouching for aero, it's brilliant.

practicalli-john12:09:58

Putting passwords in a file within a project increases the chance someone is going to commit it my mistake. This is why there are bots continually running against GitHub because people so easily make this mistake. Its never a good idea to put passwords in a file within a project. For Heroku, you can https://practical.li/clojure-web-services/projects/banking-on-clojure/deployment-via-ci.html#heroku-pipeline-configuration for a project or via the Heroku CLI tool. Creating addons such as a database will automatically add a config var, which you can then view the value in the dashboard of via the Heroku cli. Environ is a convenience library over System/getenv which allows you to use keywords rather than strings for the environment variable names. It can be used with Clojure CLI (deps.end) projects by adding it as a main dependency, :deps in deps.edn and then refereing the namespace where ever its needed. If you have multiple environments (test, stage, prod) then juxt/aero uses a hash-map wiith #profile to define values for each environment Aero can also be used with Integrant and other component services , e.g. https://practical.li/clojure-web-services/repl-driven-development/integrant-repl/#aero-tag-literals

Chase13:09:26

Awesome, thanks so much folks! Aero looks perfect for my needs as I progress past simply using calls to System/getenv

noisesmith17:09:45

for *nix systems, you can use a file with entries like

FOO_BAR=baz
QUUX=some-secret
and then start clojure using env $(< env-file) clj ... to use those vars

🙌 2
Vk03:09:46

Hey everyone, I am trying to figure out how I can access a local jar in my project using deps.edn .

:extra-deps  {foobar         {:local/root "./test.jar"}
How would I go about accessing its classes in any namespace? (:import (foobar MyImportedClass)) This does not seem to work. I couldn't find examples of accessing classes in local jars..

seancorfield04:09:26

@krishanvj you'll need to provide a bit more detail about what test.jar is/contains but https://clojure.org/guides/deps_and_cli#local_jar shows how you depend on JAR files locally so your example "should" work.

seancorfield04:09:01

Are you sure there's a foobar.MyImportedClass in test.jar?

seancorfield04:09:33

Did you use the appropriate alias when you started your REPL/project? :extra-deps has to be in an alias.

popeye07:09:30

hoe can i run inc for the collection (def a [ [1 3] [ 5 6]]) ? I tried with (apply map inc a) but this work only for (def a [[1 3]]) do I need to do doseq here?

delaguardo07:09:28

what do you expect as a result?

randomm char17:09:26

Another way would be

randomm char17:09:16

Another way to do it is

(def a [ [1  3] [ 5 6]])                
(mapv #(mapv inc %) a)

popeye07:09:59

I wanted [[2 4] [6 7]]

delaguardo07:09:40

you can use clojure.walk/postwalk for that

(clojure.walk/postwalk
 (fn [x]
   (if (number? x)
     (inc x)
     x))
 a) ;; => [[2 4] [6 7]]

popeye07:09:33

Cant we do it i using inbuilt function such as map , apply ?

delaguardo07:09:49

clojure.walk namespace provided by clojure. but you can do the same using map

(partition 2 (map inc (flatten a))) ;; => ((2 4) (6 7))
as you can see it won’t preserve original data structure

javi08:09:54

wouldn’t (mapv #(mapv inc %) [[1 3] [5 6]]) be ok?

popeye08:09:05

hmmm. that is working too!

emccue12:09:05

Generally don't use flatten though - it doesn't do what you expect

emccue12:09:18

(mapcat identity ..) is usually what you want

emccue12:09:00

but in this case you can (mapv #(mapv inc %) a), like javi suggests

Benjamin09:09:10

(doto pres
              ;; not like this because doto expands to 
              ;; (when <thing> color .. )
              (when color
                (.setItemTextForeground color))
              (.setItemText text)
              (.setTypeText type-name))
how would you express this doto but with condition? (#(when color (.setItemTextForeground % color))) like this?

delaguardo09:09:03

(doto pres
  ;; not like this because doto expands to
  ;; (when <thing> color .. )
  (cond->
      color (doto (.setItemTextForeground color)))
  (.setItemText text)
  (.setTypeText type-name))
should work

👀 2
popeye11:09:20

how to apply multiple functions to the same data in clojure ?

Ed11:09:48

you could look at juxt as in

(map (juxt inc dec) (range 5)) ;; => ([1 -1] [2 0] [3 1] [4 2] [5 3])

🙌 2
Benjamin12:09:24

is there a function like

(defn single [lst]
  (if (sequential? lst) (first lst) lst))
should return the single elm, or the first

alexmiller12:09:22

Needing this is almost always a bad sign. You should go find the case that returns a single thing and make into a coll

catjam 4
👀 6
vncz13:09:53

That’s interesting to hear. I’d like to know the motivations around this!

alexmiller13:09:44

Every time I've been down this road, I regretted it

alexmiller13:09:36

if it can ever be a coll, then it should probably always be a coll. Clojure is great for working and thinking in collection operations.

alexmiller13:09:02

doing so simplifies the logic every time you touch this data

👍 2
vncz13:09:30

Fair. I guess if you assume it’s always a collection you can thread it in other functions manipulating the data

Ed13:09:13

Stuart Sierra had a blog post about something like this that might be worth a quick read : https://stuartsierra.com/2015/06/10/clojure-donts-heisenparameter

☝️ 2
pavlosmelissinos13:09:39

I have a map, m1, and a vector of maps, vector-of-maps. I want to write a predicate that checks whether m1 shares at least one key value pair with any of the maps in vector-of-maps

(defn common-keyvals-with-any? [m1 vector-of-maps]
  (some #(not-empty (set/intersection
                      (set m1)
                      (set %))) vector-of-maps)
I've come up with the above but it doesn't pass my readability test. Is there a more idiomatic way to express the same thing?

pavlosmelissinos13:09:54

Hmm, I think what irks me the most is that the data is nested, even though it doesn't have to be. I suppose I could mapcat over the vector-of-maps to end up with flat key-value pairs, maybe even convert it to set to remove duplicates and then just compute the intersection :thinking_face:

Ed13:09:52

(let [m1             {:a 1 :b 2}
        vector-of-maps [{:c 3} {:d 4} {:a 2}]]
    (->> vector-of-maps
         (mapcat keys)
         (some (partial contains? m1))
         boolean))
maybe?

2
Ed13:09:11

I agree about turning the vector of maps into a sequence of the things that you care about, and mapcat can help there 😉

pavlosmelissinos14:09:22

That works with some slight modifications; I need to compare key value pairs, not just keys. Sorry, I should have given an example. But yeah, that approach looks much better than my original one, thanks! What I ended up with:

(defn common-keyvals-with-any? [m1 vector-of-maps]
  (-> (mapcat set vector-of-maps)
      set
      (set/intersection (set m1))
      not-empty))

Fredrik14:09:44

You could also do something like

(defn common-keyvals
  [m coll-of-maps]
  (some (set m) (apply concat coll-of-maps)))

pavlosmelissinos14:09:14

Oh damn, you're right, that works

Fredrik14:09:42

I simply love how every set in Clojure is a predicate

clojure-spin 2
vncz13:09:53

That’s interesting to hear. I’d like to know the motivations around this!

randomm char17:09:16

Another way to do it is

(def a [ [1  3] [ 5 6]])                
(mapv #(mapv inc %) a)