Fork me on GitHub
Rambabu Patina05:10:34

Hi, How to use npm modules correctly in clojurescript for the below code

import axios from 'axios';
import { aws4Interceptor } from "aws4-axios";

const client = axios.create({ baseURL: BASE_UEL, headers: {appkey: 'test'}});
        region: AWS_REGION,
        service: "execute-api",
        accessKeyId: AWS_ACCESS_KEY_ID,
        secretAccessKey: AWS_SECRET_ACCESS_KEY,
(PATH, JSON.stringify(body))


This is a snippet from when I used shadowcljs to write an 'npm' module in clojurescript - I am calling a core nodejs (net) and an npm dep (protobufjs):

(ns fnrc.core
  (:require ["net" :as net]
            ["protobufjs" :as proto]))

(def schema (atom nil))
(defn load-schema []
   (proto/load (str js/__dirname "/src/main/fnrc/proto.proto"))
   (.then #(reset! schema %))
   (.catch #(reset! schema (.toString %)))))

(defn load-schema-cb []
  (proto/load (str js/__dirname "/src/main/fnrc/proto.proto")
              (fn [err root]
                (if err
                  (reset! schema (.toString err))
                  (reset! schema root)))))


main thing is in your require area you have to quote the npm/nodejs import (the "from <whatever>" segment in your sample) - the rest would be basic clojure(script) interop for how to chain the functions as are done in your sample - and some of my snippet is not relevant to your Q, it was just the smallest sample I could find (shows promise chaining as well)

thanks3 1
Abhi Saxena16:10:29

Hi All, I have a map with optional keys - {:target_loss 980 :target_date "19-Mar-2020"} I have below spec to validated if these keys are available then target_loss is positive and target_date is a valid date. Spec doesn't seem to be working correctly, it never validates the second key target_date. What is the way to combine specs, Is there a way I can see the complete spec that is being constructed?

(def not-nil? #(not (nil? %)))
(def is-valid-date? #(and (string? %) (inst? (Date.))))
(def is-pos? #(and (nat-int? %) not-nil?))

(spec/def :int/non-negative is-pos?)
(spec/def :string/is-valid-date is-valid-date?)
(spec/def :client-supplied-request/target_loss :int/non-negative)
(spec/def :client-supplied-request/target_date :string/is-valid-date)

(spec/def :client-supplied-request/target_loss_key
  (spec/keys :opt-un [:client-supplied-request/target_loss]))

(spec/def :client-supplied-request/target_date_key
  (spec/keys :opt-un [:client-supplied-request/target_date]))

(spec/def :client-supplied-request/client-supplied-targets
  (spec/and :client-supplied-request/target_loss_key :client-supplied-request/target_date_key))
(defn is-valid-map? [spec map-input]
  (println spec/describe)
  (println map-input)
  (let [parsed (spec/conform spec map-input)]
    (if (spec/invalid? parsed)
      (throw (ex-info (format "Invalid input: %s"
                              (json/write-str (spec/explain-data spec map-input))) {:cause :bad-request}))
      (spec-tool/conform spec map-input spec-tool/strip-extra-keys-transformer))))
json-request (api-input-spec/is-valid-map? :client-supplied-request/client-supplied-targets {:target_loss 980  :target_date "19-Mar-2020"})]


is-valid-date? is calling the Date. constructor without an argument

Abhi Saxena16:10:25

even if I replace that with simple spec i.e.

:client-supplied-request/target_date :int/non-negative
it doesn't work


doesn't work in what sense? you haven't shown the output you get

Abhi Saxena16:10:12

I passes the validation even though i pass wrong input


there are several issues in the spec I pointed out, have you fixed those? are you using a different spec now? what is the different spec?

Abhi Saxena16:10:55

If I replace (spec/def :client-supplied-request/target_date :string/is-valid-date) with (spec/def :client-supplied-request/target_date :int/non-negative) then also it passes the validation with wrong input


what is the wrong input and what is :int/non-negative

Abhi Saxena16:10:48

(spec/def :int/non-negative is-pos?)

{:target_lpi_as_date_str "fff" :target_enrollment 333}


and what part of that isn't supposed to match?

Abhi Saxena16:10:28

:target_lpi_as_date_str "fff" 
this should fail since this is not an integer


your spec says the keys are optional and unqualified (:opt-un)


that key isn't in the spec you showed, so spec wouldn't do anything with it

Abhi Saxena16:10:11

:target_date "fff" 

Abhi Saxena16:10:15

this is the key


that isn't what you pasted above


are you sure that is the key?

Abhi Saxena16:10:56

Yes, I just tweaked the actual code to make it simpler


so what is the input?

Abhi Saxena16:10:48

{:target_date "fff" :target_loss 333}


and what is the spec

Abhi Saxena16:10:47

(spec/def :client-supplied-request/target_loss :int/non-negative)
(spec/def :client-supplied-request/target_date :int/non-negative)


and what is :int/non-negative

Abhi Saxena16:10:24

(def is-pos? #(and (nat-int? %) not-nil?))

(spec/def :int/non-negative is-pos?)


those are the keys in the spec, what is the whole spec?

Abhi Saxena16:10:12

(spec/def :client-supplied-request/client-supplied-targets
  (spec/keys :opt-un [:client-supplied-request/target_loss :client-supplied-request/target_date]))


I am trying to get all the pieces together in one place, because getting the parts here and there separate has resulted in lots of disjointed bits that don't match each other


Not sure if it explains the problem but the definition of is-pos? is wrong, it should be: (def is-pos? #(and (nat-int? %) (not-nil? %))


it doesn't actually matter, because the not-nil? isn't doing anything


nat-int? is already false for nil


Yes but if the input is a nat-int then not-nil? is evaluated and is-pos? returns that


sure, but a function has a truthy value of true

πŸ‘ 1
πŸ˜… 1

(require '[clojure.spec.alpha :as spec])
(import '(java.util Date))

(def not-nil? #(not (nil? %)))
(def is-valid-date? #(and (string? %) (inst? (Date.))))
(def is-pos? #(and (nat-int? %) not-nil?))

(def is-pos? #(and (nat-int? %) not-nil?))

(spec/def :int/non-negative is-pos?)

(spec/def :client-supplied-request/target_loss :int/non-negative)
(spec/def :client-supplied-request/target_date :int/non-negative)

(spec/def :client-supplied-request/client-supplied-targets
  (spec/keys :opt-un [:client-supplied-request/target_loss :client-supplied-request/target_date]))

(spec/valid? :client-supplied-request/client-supplied-targets {:target_date "fff" :target_loss 333})


is the facts as I understand it, put together in a nice neat package that anyone can paste right into a repl and try out


and spec/valid? returns false there


so, either the spec you are sharing is not what you are actually running, or you are misinterpreting results of a larger program, or ...

Abhi Saxena17:10:48

there was a typo in the key, it worked.


(inst? (Date. ...)) has the same truth value as just (Date. ...)


combining nat-int? and not-nil? is redundant, nat-int? is never nil

Muhammad Hamza Chippa21:10:19

Share it in clojurescript but I think it will be suitable for this channel too. I am trying to use react-flow in my shadow-cljs project, but can't figure out to write onNodeChanges handler. Instead of using useState I used reagent-atom and instead of callback function. I simply used atom reset but it is giving me the error Uncaught TypeError: t$jscomp$0.reduce is not a function . Here is the link for official documentation.

const [nodes, setNodes] = useState(initialNodes);
 is replaced with 
 (def nodes (r/atom initial-nodes))
 const onNodesChange = useCallback((changes) => setNodes(applyNodeChanges(changes, nodes)), []);
 is replaced with
 (defn on-node-change [changes] 
 (let [new-nodes (apply-node-changes changes @nodes) ] (reset! nodes new-nodes  ))
What I am doing wrong ?


Welcome πŸ‘‹. If you want to cross-post (usually shouldn't be necessary but :man-shrugging:), your best option is to post once, and then link to that in other channels. That directs everyone to the same place to respond. And for long posts like this, it is nice if you first post just your question, and start a thread for extra details and code. You can create code blocks by typing `. That makes your code way easier to read.


Nothing about your code example jumps out as being wrong. You probably want to narrow down where the error is happening.


What's the benefit of destructuring in functions parameters vs doing it in the body? will the destructuring happen before passing the value the to the function?


If you clojure.walk/macroexpand-all, you can see that that destructuring happens in the body of the function:

> (clojure.walk/macroexpand-all '(defn foo [{:keys [a b c]}]))
      (clojure.core/seq? map__33971)
       (clojure.core/next map__33971)
        (clojure.core/to-array map__33971))
        (clojure.core/seq map__33971)
        (clojure.core/first map__33971)
     (clojure.core/get map__33971 :a)
     (clojure.core/get map__33971 :b)
     (clojure.core/get map__33971 :c)]))))
destructuring in the function parameters can be useful to communicate a bit about the shape of the data the function accepts. As a made up example:
(defn my-parse [form {:keys [opts keywordize? on-error]}])
You can see that the second argument to my-parse can have the keys opts, keywordize?, and on-error.

πŸ‘ 1

Other than that, I don't think it matters that much. You can also go overboard in trying to communicate too much through destructuring (relying on the :or of a destructure is a common example).


thx, in what scenarios do you see the function signature and not the body? API docs?


The function signature (or arglists, there can be multiple for a single function) is used by number of tools. For my personal setup (emacs+nrepl+cider), it shows up as I'm typing.

πŸ‘Œ 1

And as you mentioned, it also gets picked up by several documentation generators.


user> (defn parse [form {:keys [opts keywordize? on-error]}])
user> (meta #'parse)
{:arglists ([form {:keys [opts keywordize? on-error]}]),
 :line 74,
 :column 7,
 :file "*cider-repl workspace/desk:localhost:60870(clj)*",
 :name parse,
 :ns #namespace[user]}


To me it just looks cleaner in the body but maybe is just my unfamiliarity

πŸ‘ 1

this is a context sensitive thing. in my current app all my web resource handlers use destructuring in the function def, but not many other functions in my code do. it's just a convention that i found useful.


In Calva, I see a full function signature, including destructuring, along with doc strings, when I hover the mouse over the name of a function. This tells me which keys my map needs to have, or gives meaningful names to parts of a tuple.


Also sometimes you have a larger map that your function only needs one or two keys from, in this case destructuring (at least as long as it is without :as) also lets the caller know that you will not be messing with the other keys in the map (say a ring request with a ton of stuff and you only care about two headers).


"in what scenarios do you see the function signature and not the body" I think any time I look at a function to find out what it does and how to use it, I want to be able to avoid a deep dive into the body, even though it is right there. I just want a quick synopsis that, again, might spare me digging into the body. Having the destructuring in the function parameter list makes the synopsis a bit richer. But, tbh, many times a function is really expecting a specific control block as a parameter. If there is no :as to communicate that, the code feels a bit obfuscated/clever.


If the caller calls the function with the wrong shape of argument, it can be difficult to know why Sometimes it’s nice while debugging a faulty caller to have the binding in the body where you can set breakpoints or print out values.