Fork me on GitHub
#beginners
<
2021-04-11
>
Oliver06:04:14

When I start a REPL with lein repl in my project directory I see the following error: #error { :cause Could not locate clj_json/core__init.class, clj_json/core.clj or clj_json/core.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name. :via [{:type clojure.lang.Compiler$CompilerException :message Syntax error compiling at (myproject/core.clj:1:1). :data #:clojure.error{:phase :compile-syntax-check, :line 1, :column 1, :source myproject/core.clj} :at [clojure.lang.Compiler load Compiler.java 7648]} {:type .FileNotFoundException :message Could not locate clj_json/core__init.class, clj_json/core.clj or clj_json/core.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name. Any hint what I am doing wrong here? deps.edn looks like this: { :deps { clj-json/clj-json {:mvn/version "0.5.3"} } }

Zaymon06:04:32

Hi, I’m just starting with Clojure. I’m struggling to think about how I should compose handlers. Is there a more idiomatic way to run validation, if it’s okay perform some side-effect and then return an appropriate response?

(def Letter
  [:map
   {:title "Letter"}
   [:id uuid?]
   [:recipient uuid?]
   [:sender uuid?]
   [:content string?]])

(defn validate-letter [letter] (me/humanize (m/explain Letter letter)))

(defn send-letter
  "Handler to send a letter to another user"
  [{{{:keys [letter]} :body} :parameters}]

  (let [letter-with-id (assoc letter :id (java.util.UUID/randomUUID))]

    (if-let [errors (validate-letter letter-with-id)]
      {:status 400
       :body {:errors errors}}

      ;; Where do I persist this thing to the database?

      {:status 200
       :body {:letter letter-with-id}})))
Coming from F# where everything is mostly done with chaining monads I’m struggling to get my head around how to express workflows with an Ok path and an Error path.

dharrigan07:04:56

I see you're using reitit and malli?

Zaymon07:04:12

Sure am 🙂

dharrigan07:04:54

There is the #reitit and #malli channels, but for now, I can tell you what I do. Malli and Retiti also use Muunjata for validation (part of the suite). If you define a spec, and it fails the spec (i.e., validation), an appropriate error will be returned to the client. However, for more business-oriented validation, I have my own excpetion middleware

dharrigan07:04:02

that I place in the chain

dharrigan07:04:44

then deeper down in the layers if I encounter an error, like some business contraint violation, I throw and let the exception handler catch, log out to Sentry, but also format a lovely response back to the client.

Zaymon07:04:29

Aha. Okay that would be great to move the validation up a layer. I’ll look into it

dharrigan07:04:40

I should extend my simple github example to use some validation

dharrigan07:04:53

but for now, here's a quick example:

dharrigan07:04:37

today, you've given me reason to add in some simple validation 🙂

Zaymon07:04:26

Sweet I’ll look out for the changes too.

dharrigan07:04:32

in my business application, I have this:

dharrigan07:04:10

:middleware [swagger/swagger-feature
                        muuntaja/format-middleware
                        (exceptions/exception-middleware app-config)
                        parameters/parameters-middleware
                        coercion/coerce-exceptions-middleware
                        coercion/coerce-request-middleware
                        coercion/coerce-response-middleware]}}))

dharrigan07:04:23

notice the (exceptions/exceptions-middleware ....)

dharrigan07:04:28

that's my own middleware handler

Zaymon07:04:54

Did you just need more control over how exceptions were presented?

dharrigan07:04:23

yes, I wanted to give back to the client lots more details, and a nicely crafted response.

1
Hagenek10:04:17

Hi everyone, hope you are having a nice sunday. I am struggling to understand this destructuring. It reminds me of the syntax used when we require namespaces. Is it basically to get a variable flash which the value associated with the :flash key on the request map, and also keeping the request variable as the map provided as an argument to the home-page function?

(defn home-page [{:keys [flash] :as request}]
  (layout/render
   request
   "home.html"
   (merge {:messages (db/get-messages)}
          (select-keys flash [:name :message :errors]))))

pavlosmelissinos10:04:36

Yes, that's another way of saying:

(defn home-page [request]
  (layout/render
   request
   "home.html"
   (merge {:messages (db/get-messages)}
          (select-keys (:flash request) [:name :message :errors]))))

😃 1
raspasov11:04:47

@USMLDU9BR exhaustively useful guide to Clojure destructuring 🙂 https://clojure.org/guides/destructuring

☝️ 1
roelof11:04:51

just jumping in to say hello and I hope everyone is still healty

🙃 3
Dave Suico15:04:49

Hi guys, does anyone know how to fix an error message like this when u try to install polylith as a dependency? "Error building classpath. Manifest type not detected when finding deps for polyfy/polylith in coordinate"

David Reno16:04:32

I’m trying to figure out when to use the ! suffix on functions. Do I use it on any function that is impure? The docs aren’t super clear to me as they reference functions that “are not safe in STM transactions” which I’m not clear about. Also, does everything that calls this function also get the bang? Here’s my sequence of functions in question for background:

(defn get-register!
  "return the byte contents of a register (action)"
  [bus port register]
  (let [address (get-in registers [port register])] ;; lookup register address
    (smbus/read-byte bus address)))                 ;; return the value there

(defn get-registers!
  "return the byte contents of both :a and :b registers (action)"
  [bus register]
  {:a (get-register! bus :a register)
   :b (get-register! bus :b register)})

(defn get-int-registers!
  "Return all the registers needed to deduce what changed related to an
  interrupt occurence (action)"
  [bus]
  (do ;; order of reads is important
    (def intf (get-registers! bus :intf))
    (def intcap (get-registers! bus :intcap))
    (def gpio (get-registers! bus :gpio))
    {:intf intf :intcap intcap :gpio gpio}))

Zaymon01:04:38

I use ! to denote this function has a side-effect or mutates some state. It’s a nice little reminder when using any ! function that you should be aware it’s not a pure transformation.

👍 1
practicalli_john16:04:47

I use it on functions that actually do a mutation or major side-effect. I don't use the ! for functions that call functions that do the mutation.

👍 1
David Reno19:04:57

Thanks everyone. I’ve decided not to propagate the exclamation mark but I’ll have to think more about the first read because it’s not idempotent, it will actually cause the next read to be different. I may use it there.

dpsutton16:04:26

clojure uses the STM in loading libraries and possibly other functions, so it is true that every time i use clojure i use the STM. Outside of that though, I've never directly used it in code that i've consumed or code that i've written. I wouldn't use the STM's conventions in naming affect your naming choices. That being said, people often use ! as a suffix when writing mutating impure functions. Add it as you like

👍 3