Fork me on GitHub
#clojure
<
2020-05-15
>
Spaceman00:05:55

How can I write the following macro?:

(db-event :add-to-cart [product-id] {:cart {product-id (-> db :cart (get product-id) inc)}})
should give
(db-event :add-to-cart [product-id]
     (assoc-in db [:cart product-id] (-> db :cart (get product-id) inc)))
   and
(db-event :foo [resp] {:abc {:def resp :efg (more-stuff resp)} :foo1 {:bar1 (stuff resp)}})         
should give
(db-event :foo [resp]
          (->
           (assoc-in db [:abc :def] resp)
           (assoc-in [:abc :efg] (more-stuff resp))
           (assoc-in [:foo1 :bar1] (stuff resp))))
so basically doing a bunch of assoc-in, just giving a map of all the things that are to be changed. and hiding any reference to the original map db except when it's used in a value of the updated

Spaceman00:05:03

if it helps, I'm trying to extend the db-event macro which is this:

(defmacro db-event [event-key params & body]
`(do
     (re-frame.core/reg-event-db ~event-key
                                 (fn [~'db [_# ~@params]]
                                   ~@body))
     (defn ~(symbol event-key) ~params (re-frame.core/dispatch [~event-key ~@params]))))

hiredman01:05:30

Don't write a macro

Spaceman01:05:39

because it's hard?

hiredman01:05:25

No, because functions are more reusable

Spaceman01:05:01

Okay, but I haven't found the need to reuse them really after 1500 lines worth of reframe events. Maybe I'm missing out somehow?

hiredman01:05:27

A function is basically always better than a macro

hiredman01:05:49

And if you had a function you could write a macro in terms of the function

hiredman01:05:11

But the reverse is not true

Spaceman01:05:23

whereas the macro cuts down the number of lines of the events file to 500, maybe even less

Spaceman01:05:23

okay, I'm willing to face the consequences. If I need to reuse anything, I can always macro expand. But how will I write it though?

hiredman01:05:45

A function removes duplication just as much as a macro

Spaceman01:05:23

I really don't want to debate the pros and cons. I'm looking for the solution of the question I asked

seancorfield01:05:55

You asked all these questions in #beginners @pshar10 Please do not repeat questions here that you've already asked in #beginners

potetm01:05:11

But @pshar10. We’re developers. Debating pros and cons is what we do!

seancorfield01:05:15

It's rude and it wastes people's time.

dpsutton04:05:18

question about depstar if anyone is familiar with that codebase (in thread).

dpsutton04:05:22

The following code calls copy source:

(defmethod copy-source*
  :jar
  [src dest options]
  (when-not (= :thin (:jar options))
    (when *verbose* (println src))
    (consume-jar (path src)
      (fn [inputstream ^JarEntry entry]
        (let [^String name (.getName entry)
              last-mod (.getLastModifiedTime entry)
              target (.resolve ^Path dest name)]
          (if (.isDirectory entry)
            (Files/createDirectories target (make-array FileAttribute 0))
            (do (Files/createDirectories (.getParent target) (make-array FileAttribute 0))
              (try
                (when (.startsWith name "META-INF/versions/")
                  (reset! multi-release? true))
                  ;; name is the filename in jar, inputstream is a JarInputStream
                (copy! name inputstream target last-mod) ;; note the inputstream here
                (catch Throwable t
                  (prn {:error "unable to copy file"
                        :name name
                        :exception (class t)
                        :message (.getMessage t)})
                  (swap! errors inc))))))))))
which iterates over the entries in a jar:
(defn consume-jar
  [^Path path f]
  (with-open [is (-> path
                     (Files/newInputStream (make-array OpenOption 0))
                     .BufferedInputStream.
                     JarInputStream.)]
    (loop []
      (when-let [entry (.getNextJarEntry is)]
        (f is entry)
        (recur)))))
And in copy!
(defn copy!
  ;; filename drives strategy
  [filename ^InputStream in ^Path target last-mod]
  (if-not (excluded? filename)
    (if (Files/exists target (make-array LinkOption 0))
      (clash filename in target)
      (do
        (Files/copy in target ^"[Ljava.nio.file.CopyOption;" copy-opts)
        (when last-mod
          (Files/setLastModifiedTime target last-mod))))
    (when *debug*
      (prn {:excluded filename}))))
My question is about the Files/copy there which has the docstring: > Copies all bytes from an input stream to a file. On return, the input stream will be at end of stream. So it seems it should drain that whole inputstream which is the JarInputStream. So I'm confused why it isn't stuffing the whole jar into the destination at each filename. It seems the input stream is the whole jar, the jar entry is the file in the jar. but copy doesn't copy the jar entry, it creates a target in the destination jar ( (.resolve ^Path dest name) ) but then copies that whole jarinputstream to it. Does anyone see my error?

hiredman04:05:51

Jarinpustreams are odd

hiredman04:05:38

A jarinputsream is, if I call, sort of a mix between and iterator and an inputstream

dpsutton04:05:55

I was wondering if it copies according to what the getnextentry kinda does something weird to it

hiredman04:05:45

Yes, Everytime you call getnextentry, the jarinpustream sort of resets as the inputstream for the next entry

hiredman04:05:44

The zip file stuff (which is where I remember this from) works the same way

seancorfield04:05:35

Just noticed this thread 🙂

dpsutton04:05:43

i kinda knew you would jump in except that you got beaten to the punch. thanks though @U04V70XH6

seancorfield04:05:32

Yeah, I must admit, I was a bit puzzled by the original code but figured that it must behave along the lines that @U0NCTKEV8 explained, in that the input stream is "chunked" to match the files in the JAR file, so a single input stream can be used across multiple logical files...

seancorfield04:05:52

I'm curious @U11BV7MTK what made you dig into the source?

dpsutton04:05:02

yeah i was surprised that the entries aren't JarEntryInputStreams or something similar.

dpsutton04:05:04

i've never been heavy interop or know jar related bits so what better way to refresh than by reading ghadi code

dominicm09:05:02

How can I do some basic static image generation with a bg color, image overlay & some text? I reached for quil, but couldn't figure out things like producing an outputstream rather than a window or file.

phronmophobic16:05:03

here’s an example:

(ns membrane.example.todo
  (:require [membrane.skia :as skia]
            [membrane.ui :as ui
             :refer [image
                     translate
                     label]])
  (:gen-class))

(defn overlay []
  (let [img (ui/image "resources/Clojure_logo.svg.png"
                      [400 nil])]
    [img
     (translate 50 50
                (ui/label "This is a clojure logo"
                          (ui/font "/Library/Fonts/Comic Sans MS.ttf"
                                   45)))]))

(defn -main [& args]
  (skia/draw-to-image! "myoverlay.png" (overlay)))

dominicm17:05:30

https://github.com/phronmophobic/membrane/blob/master/project.clj#L7-L30 that's a lot of dependencies - a surprising set really. I don't think figwheel should be in there?

phronmophobic18:05:11

you’re* right

dominicm18:05:37

Will take a look though, it'd be nice to have cljs support too.

dominicm18:05:50

It's not necessary, we have no specific plans, but it's a nice idea.

phronmophobic18:05:16

i’m not sure what you mean by cljs support

phronmophobic18:05:27

but there is a graphics backend for webgl

dominicm18:05:00

That's what I mean :). The java2d recommendation doesn't have cljs support (obviously). So using a library that does is positive (like yours).

phronmophobic18:05:43

feel free to ping me if you have any questions. feedback is welcome!

phronmophobic18:05:42

fyi, just pushed a new version to clojars [com.phronemophobic/membrane "0.9.9-beta-SNAPSHOT"] that moves figwheel to dev dependencies.

dominicm18:05:59

Cool, thanks :) I think we'll get to this next week.

👍 4
rickmoynihan11:05:29

@dominicm Not used quil for or the image processing side of java for years; but IIRC on the JVM you probably want to sniff around java.awt.image.BufferedImage as I remember that class is used in some of the underyling swing/awt stuff that quil is probably using.

rickmoynihan11:05:11

Not sure if that’s useful thread to pull on; but without digging it’s my first thought.

👍 4
rickmoynihan11:05:43

Also you could possibly look at SVGs / Batik etc

rickmoynihan11:05:55

not sure if there’s a clojure interface over it

dominicm14:05:23

that looks handy, definitely an option I hadn't seen.

Cas Shun15:05:43

I have an array of byte arrays, (class arr) => [[B , I'd like to type-hint this to eliminate reflection when using aset. I'm unsure of how to do this. (simple code example incoming)

Cas Shun15:05:50

(set! *warn-on-reflection* true)
(def b1 (byte-array [(byte 0x43)
                     (byte 0x6c)
                     (byte 0x6f)
                     (byte 0x6a)
                     (byte 0x75)
                     (byte 0x72)]))
(def arr (make-array Byte/TYPE 1 6))
(aset arr 0 b1)
;; Reflection warning - call to static method aset on clojure.lang.RT can't be resolved (argument types: unknown, int, unknown).

Cas Shun15:05:45

no, I didn't realize that was possible

Cas Shun15:05:07

and that does it 🙂

Brian15:05:03

What is the standard for parsing the output of tests in a CI/CD pipeline? For instance in my terminal I can run clojure -A:test -m my-ns.test and that might produce an output like

FAIL in (id-exists) (ids.clj:57)
expected: false
  actual: false

Ran 26 tests containing 29 assertions.
1 failures, 0 errors.
What is the standard way to determine if there is a failure and then reporting that failure? I could grep for the word FAIL. Wondering if there is another standard as that doesn't seem totally right

seancorfield16:05:46

The exit status of the clojure command will be 0 for success and non-zero for failing tests. You can check $? in the shell.

seancorfield16:05:45

Some CI/CD systems handle that automatically for you (and abort when any command exits with a non-zero status).

seancorfield16:05:27

(in both cases, the abort is automatic on test failures)

Brian16:05:46

Thank you @U04V70XH6! I'll take a look at this =]

Brian18:05:33

@U04V70XH6 for me $? is always 0 after test failures in my terminal. We are using Jenkins and Groovy to run our tests. Right now I was using groovy's sh command to turn the test command and that is returning 0 for me on failures. Any ideas for me in this environment?

Brian18:05:22

I'm not the most familiar with Jenkins so I may be getting something wrong. But CircleCI and Github Actions seem like competitors to Jenkins rather than a plugin I could be using here

seancorfield22:05:49

@UN1E6R822 Are you using an up-to-date version of the Cognitect test-runner? Early versions did not set the exit status on failure.

seancorfield22:05:42

(! 662)-> clj -A:new lib brian/test-failure
Generating a project called test-failure based on the 'lib' template.
The lib template is intended for library projects, not applications.

Fri May 15 15:12:19
(sean)-(jobs:0)-(~/clojure)
(! 663)-> cd test-failure/

Fri May 15 15:12:23
(sean)-(jobs:0)-(~/clojure/test-failure)
(! 664)-> clojure -A:test:runner

Running tests in #{"test"}

Testing brian.test-failure-test

FAIL in (a-test) (test_failure_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.

Fri May 15 15:12:34
(sean)-(jobs:0)-(~/clojure/test-failure)
(! 665)-> echo $?
1
To confirm it works ^ and here's the deps.edn from that newly-created project:
(! 666)-> cat deps.edn 
{:paths ["src" "resources"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}}
 :aliases
 {:test {:extra-paths ["test"]
         :extra-deps {org.clojure/test.check {:mvn/version "1.0.0"}}}
  :runner
  {:extra-deps {com.cognitect/test-runner
                {:git/url ""
                 :sha "f7ef16dc3b8332b0d77bc0274578ad5270fbfedd"}}
   :main-opts ["-m" "cognitect.test-runner"
               "-d" "test"]}
  :jar {:extra-deps {seancorfield/depstar {:mvn/version "1.0.94"}}
        :main-opts ["-m" "hf.depstar.jar" "test-failure.jar"]}
  :install {:extra-deps {deps-deploy {:mvn/version "0.0.9"}}
            :main-opts ["-m" "deps-deploy.deps-deploy" "install" "test-failure.jar"]}
  :deploy {:extra-deps {deps-deploy {:mvn/version "0.0.9"}}
           :main-opts ["-m" "deps-deploy.deps-deploy" "deploy" "test-failure.jar"]}}}
I know nothing about running shell commands via Groovy, sorry (why are you doing that? can't you run the clojure command directly on Jenkins?)

hiredman02:05:19

Likely the groovy process isn't exiting with the same status as the clojure process it shells out to

hiredman02:05:09

I would just use, uh, I think Jenkins calls them freestyle projects? It has been a few years

hiredman02:05:43

Having groovy in the mix is just another thing to go wrong

hiredman02:05:14

The docs seem to claim on the latest version of the groovy stuff and non-0 statues will cause it to throw an exception

plins15:05:22

is there a more idiomatic way of doing this? (if % (float %) %)

thumbnail15:05:41

(some-> % float) should work.

😯 4
8
plins15:05:32

thanks 🙂

noisesmith16:05:26

also there are few reasons to use float rather than double

plins16:05:22

I guess I can use double thanks 🙂

🙃 4