Fork me on GitHub
#clojure
<
2017-01-28
>
creese00:01:27

How can I redef a spec for a unit test?

eslachance03:01:13

I'm having a bit of trouble with deploying to clojars (windows install). When I ran lein deploy clojars it asks for credentials then I got:

Could not sign D:\home\devel\dithcord\target\dithcord-0.0.8.jar
gpg: no default secret key: secret key not available
gpg: signing failed: secret key not available
Tried to gpg --gen-key, filled in all the blanks, tried again, got:
Could not sign D:\home\devel\dithcord\target\dithcord-0.0.8.jar
gpg: cannot open tty `no tty': No such file or directory
However, I (mistakenly) did this in another project (had 2 windows opened) and it worked flawlessly. anyone know how I can fix this?

eslachance03:01:07

(ironically the project that works doesn't have org.clojars.username/project in the defproject)

geek-draven10:01:06

I'm having some trouble chaining some functions together. I have a function that makes an API call, get's back the data and then calls another function, which uses FOR to clean up the response and pass the data onto a final function to do a batch insert into a database. If I call this on it's own it works fine, but if I try and run it in a function with the other 2 versions of this, I don't get any errors, I added a pprint to the start of each individual function so I can see they all run, but only the final function writes to the database.

nblumoe10:01:08

@geek-draven can you show the implementation?

nblumoe10:01:00

maybe run into some issues with lazy seqs not being realised.... however, just wildly guessing without having the code 😉

geek-draven10:01:04

@nblumoe sure, this the workflow:

geek-draven10:01:07

(defn insert-dashboard [data] (try (j/insert! db :ga_dashboard [:campaign :date :medium :source :adClicks :adCost :impressions :transactionRevenue :sessions :transactions :pageviews :bounces :sessionDuration] data) (catch Exception e (str "caught exception: " (.getMessage e))))) (defn clean-dashboard [data] (println "inserting data into dashboard") (for [idx (range 0 (count data))] (let [campaign (get-in (nth data idx) [0]) date (get-in (nth data idx) [1]) medium (get-in (nth data idx) [2]) source (get-in (nth data idx) [3]) adClicks (get-in (nth data idx) [4]) adCost (get-in (nth data idx) [5]) impressions (get-in (nth data idx) [6]) transactionRevenue (get-in (nth data idx) [7]) sessions (get-in (nth data idx) [8]) transactions (get-in (nth data idx) [9]) pageviews (get-in (nth data idx) [10]) bounces (get-in (nth data idx) [11]) sessionDuration (get-in (nth data idx) [12])] (insert-dashboard (conj [] campaign date medium source adClicks adCost impressions transactionRevenue sessions transactions pageviews bounces sessionDuration))))) (defn data-export [view-id metrics dimensions] (println "generating report") (->> (results view-id metrics dimensions) :records (map #(mapv :value %)))) (defn dashboard [] (-> (data-export view dashboard-metrics dashboard-dimensions) (clean-dashboard)))

geek-draven10:01:30

then I'm calling the 3 like this:

geek-draven10:01:31

(defn run-reports [] (dashboard) (Thread/sleep 5000) (vbc) (Thread/sleep 5000) (redirect))

nblumoe10:01:29

Would be great if you could wrap the code in ` so that slack format that more nicely. Could you try if it works when you change (dashboard) to (doall (dashboard))?

nblumoe10:01:52

You could also put a println into insert-dashboard to check if that actually gets called (I think it does not because the lazy seq returned by for in clean-dashboard does not get realised)

geek-draven10:01:23

@nblumoe Thanks, that worked 👍 Also, thanks for the tip about ` - didn't know that either

nblumoe10:01:07

Ok great. Is it clear to you why you ran into that issue? The side effects of inserting into the DB not being triggered within the for? And also why this would work when running (dashboard) on the repl?

geek-draven10:01:14

Not really, I think I need to do some more reading up on lazy functions - as a rule I try and avoid for as it tends to cause issues

geek-draven10:01:39

lazy sequence, I mean

geek-draven10:01:04

@nblumoe after reading up on doall on clojuredocs, it seems clearer now - I assume that the first functions weren't actually completing as I was returning a lazy sequence, but by running doall, it forced it to complete

nblumoe10:01:55

exactly. usually lazy seq behave as you would want them to as long as you are using them as values for something. However, as in your case, when you have some side effect (like inserting into a db, changing some state, printing, etc.) inside a lazy seq, the side effect might not happen at all and not completely (in the end depending on how much of the lazy seq actually gets realised “downstream”)

nblumoe10:01:38

doall forces it to be realised. calling a function that returns a lazy seq on the REPL also realises it because the REPL then prints the seq

nblumoe10:01:57

When using Clojure I would encourage you not to avoid lazy seqs at all, because they are actually very nice. You just need to be aware of dealing with side effects when needed and maybe be aware of infinite seqs (e.g. not printing them 😉 )

geek-draven10:01:32

@nblumoe Thanks, that's really helpful

geek-draven10:01:57

@nblumoe Clojure's the first language I've learnt and only been doing it for 18 months

nblumoe10:01:00

btw what I proposed was a bit of a quick hack. in the end the cleaner solution would be to replace your for list comprehension with a doseq

nblumoe10:01:15

Great choice for your first language. keep going! 🙂 I would like to encourage you to try to understand concepts that you encounter (like lazy seqs) in depth. In Clojure you don’t have that many core concepts that you need to learn about so this does not have to be intimidating or something like that

janimatti12:01:50

Hi, first of all, I want to state that I'm fairly new to clojure and I'm trying to learn as I develop my web app (and not really by only experimenting with repl). I'm trying to save data obtained from an API endpoint to a local MySql database. I have obtained the data from the API but I stuggle a bit with transforming the data into a data structure that my luminus web app accepts. The issue I have is that some db columns do not match 1:1 with the field names in the API data and that some expected fields are missing in the API data. The following code works IF the to-be renamed field is found in the data. Unfortunately certain fields are missing in some of the items in the collection. This is the problem I'm currently dealing with.

; all fields (keys) named missing and missing_description need to be renamed to is_lost and is_lost_description
(def renamedData (map
  #(postwalk-replace % discsParsed)
  [{:missing :is_lost, :missing_description :is_lost_description}])) 
A friend of my provided me with the following, partial solution:
(conj data {:is_lost_description "" :another_key "foo" :a_third_key "bar"})
However, I don't know, which keys are missing so I need to check that for each item in the collection. In PHP I would just have stored changes to a local temporary variable but afaik, this is not the way you should do things in clojure. Would appreciate some help, cheers! 🙂

beppu13:01:24

@janimatti Maybe something like this could help:

(defn rename-keys
  "Rename keys in a map"
  [original new-names]
  (reduce-kv (fn [m k v]
               (if (new-names k)
                 (-> m
                     (assoc (new-names k) v)
                     (dissoc k))
                 m))
             original
             original))
Use it like this:
(rename-keys {:missing 88 :missing_description "oh no"} {:missing :is_lost, :missing_description :is_lost_description})
{:is_lost 88, :is_lost_description "oh no"}

janimatti14:01:28

thanks! I'll try it as soon as I have time for some clojuring! 🙂

lmergen14:01:05

hey, my google fu is letting me down — i’m finally exploring the world of macros a bit, and am wondering what the :forms parameter is that seems to be provided sometimes. where can i find more about it ?

tbaldridge16:01:24

@lmergen it's not really documented, but macros take two additional (silent) parameters, forms and env. env is a hashmap of locals that exist in the current context to Compiler internal objects that represent those locals. The values are mostly useless

tbaldridge16:01:42

Forms is the bare data handed to the macro, not the arguments but the form itself.

tbaldridge16:01:37

user=> (macroexpand '(defmacro foo [x y] nil))
(do (clojure.core/defn foo ([&form &env x y] nil)) (. (var foo) (setMacro)) (var foo))

sakalli19:01:15

hi, trying to learn.

clojure-noob.core> ((fn [v coll]
  (reduce
   #(assoc %1 %2 v)
   {}
   coll))
   1
   [:a :b :c])
returns
{:a 1, :b 1, :c 1}
but this throws an exception
clojure-noob.core> ((fn [v coll]
   (reduce
    #(assoc {} %1 v)
    coll))
 1
 [:a :b :c])
ArityException Wrong number of args (2) passed to: core/eval1783/fn--1784/fn--1785  clojure.lang.AFn.throwArity (AFn.java:429)
even though this does not
clojure-noob.core> (#(assoc {} %1 %2) :a 3)
{:a 3}
why is that?

tom19:01:47

@sakalli that example with the ArityException using the function shorthand accepts one argument. The shorthand uses the highest argument (`%n`) to set the arity it expects.

joshjones19:01:17

@sakalli an example of not using the #() shorthand, which ignores the second argument:

((fn [v coll]
   (reduce
     (fn [arg1 _] (assoc {} arg1 v))
     coll))
  1
  [:a :b :c])

sakalli20:01:32

thanks for helping. admittedly still struggling. the @joshjones example evaluates to:

{{:a 1} 1}
rewriting the first example without the shorthand works as desired:
((fn [v coll]
   (reduce
    (fn [arg1 arg2] (assoc arg1 arg2 v))
    {} coll))
 1
 [:a :b :c])
{:a 1, :b 1, :c 1}
if i redo what i did above without the shorthand it would be like this:
((fn [v coll]
   (reduce
    (fn [arg1] (assoc {} arg1 v))
    coll))
 1
 [:a :b :c])
I get the same ArityException but I was under the impression that both in the case with the shorthand and without i am passing only one argument to the anonymous function that should be also expecting only one. Am I getting this wrong? Could it be that assoc function is expecting three arguments and does not recognize the empty map as one?

joshjones20:01:05

@sakalli the reducing function must accept two arguments. assoc will take a map, a key, and a value. Your (assoc {} arg1 v} is nonsensical in a reducing situation since you are assoc'ing to an empty map every time.

sakalli20:01:40

ah.`{} coll` are for the reduce not for the anonymous, duh. might be getting there. thank you 🙂

mobileink20:01:00

gen-class v. reify. case: implementing alexa skills in clj, running on google app engine. you inplement a speechlet, then subclass SpeechletServlet, then, in you subclass's ctor, inject your speechlet into the super SpeechletServlet. you can do this using reify to create your speechlet, or you can genclass it. i'm guessing gen-class will give faster startup times, but i'm not really sure about the pros and cons in this case. which strategy is better?

bcbradley23:01:14

when would seque be useful?