Amazing. Thank you!
Ctrl-C does not interrupt the loop for you when you run lein repl
? What OS are you using, and what is the output of lein version
? Ctrl-C interrupts evaluation for me with macOS 10.14.x and lein version
2.9.5
how can I add a (print) in this code to inspect balances value?
Short answer: (if-let [balances (doto (b/list-balances (:account context)) println)]
Generally I use tap>
rather than println
, with Reveal's tap hooked up
is there a quick and easy way to get a relative path and generate a full path?
(absolute path)
reveal looks awesome tks
where can I put this `
:reveal {:extra-deps {vlaaad/reveal {:mvn/version "1.3.206"}}
:ns-default vlaaad.reveal
:exec-fn repl}
in my project.clj?(.getAbsolutePath (io/file filename))
works
also consider https://docs.oracle.com/javase/8/docs/api/java/io/File.html#getCanonicalPath-- which will resolve . and .. etc
I’m trying to output this log/debug on my repl
[clojure.tools.logging :as log]
any ideas of how to fix it?I’ve tried this config
look above, it looks like you're working inside pedestal?
it is pedestal the code is here https://github.com/runopsio/sequence
(ns your.project.ns
(:require [io.pedestal.log :as log]))
(log/info "This is a test" {:key-1 "my-value})
that will send to log4j configured w/ pedestal
this is a good trick
tks
also, the tap> function is pretty useful if you want to send a message to a backend queue, and provide different models to handle it
Hello everybody I am using some vars like:
(def status.scheduled 1)
(def status.canceled 2)
But when I’m trying to use them into a function I’m getting a syntax error (`Syntax error (ClassNotFoundException) compiling at (src/clj/awp/services/appointment.clj:3:8).`
status.scheduled
).
(def scheduled-or-scheduled
[{:keys [status] :as appointment}]
(some #{status} [status.scheduled status.canceled]))
I found that I need to use the fullnamespaced name awp.services.appointment/status.scheduled
in order to get it work but, is there a nicely way to call this vars?I have {:reveal {:dependencies [[vlaaad/reveal "1.3.206"]]}}
in my ~/.lein/profiles.clj
, and use with lein with-profile +reveal repl
and then in the REPL (do (require '[vlaaad.reveal :as reveal]) (add-tap (reveal/ui)))
I thought maybe you were just using a bad variable name, but now I’m not sure I understand what you’re doing. Is status.scheduled
something defined by a Java lib you are using?
If not, you might be better off using a namespaced keyword like :status/scheduled
instead of using a symbol with a dot in the name.
No, they are constants from my DB. I want to group them by some way becase I also have
(def type.interview 1)
(def type.ordinary-session 2)
(def type.extraordinary-session 3)
Ok, you may be running into problems because the '.'
in the middle of a symbol has special meaning to the Clojure compiler.
We normally used namespaced keywords for that sort of thing, so it’s not usually a problem.
The (tap>) can be consumed by adding (add-tap f), where f is a function which will be called with the arguments sent to the (tap>)
thus, the function passed to add-tap can be a pedestal log, another with a console print, or into some future endpoint
decoupling the produce/consume
Ok, I see the problem. Thanks a lot!
I’m gonna read more about tap
I’m pretty new to clojure in general
me as well
Once you're aware of tap, you'll see it used often in really useful areas
for example:
https://clojureverse.org/t/introducing-shadow-cljs-inspect/5012
thank you. That’s exactly what I need
(.getAbsolutePath (io/as-file “your path”))
Why as-file
over file
?
I’m stuck in how to run reveal from this lein project
what is your project.clj?
and also what editor are you using to invoke lein?
Here is how i'm using tap> with pedestal logging, while keeping the door open to additional tap consumers going forward:
(ns my.logging
(:require [io.pedestal.log :as log]))
(defn tap->logback [content]
(let [{:keys [level message context]} content]
(cond
(= level :info) (log/info message context)
(= level :debug) (log/debug message context))))
;; Configure this handler to consume from the tap.
(add-tap tap->logback)
(ns my.handler)
(def my-handler {
:name :my-handler
:enter
(fn [context]
(let [request (:request context)]
(tap> {:level :debug :message "" :context request})))})
thats a simple example
when you want to disable that pedestal logging, you may remove-tap.
and you may add/remove it at runtime
I’m gonna try this inspect
inspect is part of shadow-cljs, and is for clojurescript
uses the same tap mechanism though, but the implementation is something akin to javascript websocket -> browser display
would anyone be willing to take a look at a babashka script I wrote? Just looking for ideas where I could have done things better https://pastebin.com/Qa5JSeGy
thank you!
Cider question: Is there a way to jump to repl from the buffer?
m-x cider-switch-to-repl-buffer
. i think it already has a default key binding
ah nice. Thank you
I’m trying to create a set based on lines from a file. Is this a valid approach?
(def co_set #{})
(with-open [rdr (reader "customer_numbers.txt")] (doseq [line (line-seq rdr)] (def co_set (conj co_set line))))
you are def co_set on every seq.
I’m assuming that wouldn’t replace co_set?
i'd reach for into
here:
(into #{} (line-seq rdr))
it would, but in my experience non-top-level def
s are frowned-upon
Hi, I have the following spec for the below function. I want to validate that the passed in menu contains the item requested in the order item. My goal is to remove the nil checks and throw exception in my function code. My state looks like the following. If I pass in invalid input, lets say I pass in an order-item with a name that does not exist in my menu, the fdef does not validate that. Any idea what I am doing wrong in the fdef?
{:orders {:e4d55743-c964-48e8-9cd1-cb1cf9075617 [#:helloworld.restaurant{:name "chilly parotta",
:quantity 3}
#:helloworld.restaurant{:name "masal dosa",
:quantity 2}]},
:menu {:kothu-parotta #:helloworld.restaurant{:name "Kothu Parotta", :price 9.5, :quantity 50},
:chicken-biriyani #:helloworld.restaurant{:name "Chicken Biriyani",
:price 10.5,
:quantity 50},
:plain-dosa #:helloworld.restaurant{:name "Plain Dosa", :price 9.5, :quantity 50},
:butter-naan #:helloworld.restaurant{:name "Butter Naan", :price 3, :quantity 50},
:paneer-butter-masala #:helloworld.restaurant{:name "Paneer Butter Masala",
:price 9.5,
:quantity 50},
:mutter-paneer #:helloworld.restaurant{:name "Mutter paneer", :price 10.5, :quantity 50},
:masal-dosa #:helloworld.restaurant{:name "Masal Dosa", :price 8.5, :quantity 48},
:chilly-parotta #:helloworld.restaurant{:name "Chilly Parotta", :price 9.5, :quantity 47},
:mutton-biriyani #:helloworld.restaurant{:name "Mutton Biriyani",
:price 10.5,
:quantity 50}}}
(defn update-menu-item-for-order-item [menu order-item]
(let [name (::name order-item)
item-id (keyword (slugify (::name order-item)))
order-quantity (::quantity order-item)]
(update-in menu
[item-id]
(fn [item]
(if (nil? item)
(throw (IllegalArgumentException. (str "Sorry, we don't have " name " in our menu")))
(if (< (::quantity item) order-quantity)
(throw (IllegalArgumentException.
(str "Not enough " (::name item) ", we are short by "
(- order-quantity (::quantity item))
" . Please correct the order and re-submit")))
(assoc item ::quantity (- (::quantity item) order-quantity))))))))
(s/def ::name string?)
(s/def ::price number?)
(s/def ::quantity number?)
(s/def ::order-item (s/keys :req [::name ::quantity]))
(s/def ::order-items (s/coll-of ::order-item))
(s/def ::menu-item (s/keys :req [::name ::price ::quantity]))
(s/def ::menu-item-id keyword?)
(s/def ::menu (s/map-of ::menu-item-id ::menu-item))
(s/fdef restaurant-save-menu :args (s/cat :menu-items (s/coll-of ::menu-item)))
(s/fdef update-menu-item-for-order-item
:args (s/and (s/cat :menu ::menu :order-item ::order-item)
#(contains? (:menu %) (keyword (slugify (::name (:order-item %))))))
:ret ::menu)
(stest/instrument `update-menu-item-for-order-item)
“If this is something that you would previously — before Clojure, before Spec — written as explicit checks in production with either error handling or exceptions, then that’s how you should write it now, in Clojure — with Spec if you want to leverage its ability to validate data.” > Lets say I want explicit validation of function arguments in some edge function in production. Is it recommended to still define specs and then use s/conform or s/valid? s/explain-data to write the validation logic instead of regular clojure code?
If you have a non-trivial data structure and you use it in multiple functions, yes, I’d say that’s a good case for Spec.
We use Spec to validate API input arguments, so that’s a case where each function might have a different set of :params
from a Ring request.
(we used to use coercions in our Specs but that is bad practice so we’ve shifted to using exoscale/coax
to do the coercions — it deduces them from a Spec — and then use a plain old Spec to do the actual conforming/validating part)
Re: 1. above — we don’t have anything we can share but this is the bulk of how it works:
(defn invalid-argument
([req spec fail-code-map]
(forbidden req "Invalid Argument"
(when fail-code-map
(->> (:params req)
(s/explain-data spec)
(problems->fail-codes fail-code-map)))))
([req fail-code]
(forbidden req "Invalid Argument" (if (vector? fail-code) fail-code [fail-code])))
([req]
(forbidden req "Invalid Argument")))
(defmacro with-validated-params
[[spec fail-code-map] & body]
`(let [~'params (s/conform ~spec (:params ~'req))]
(if (s/invalid? ~'params)
(invalid-argument ~'req ~spec ~fail-code-map)
(do ~@body))))
forbidden
just constructs a 403 HTTP response, problems->fail-codes
is a set of proprietary heuristics that maps Spec failures to entries in the fail-code-map
.Re: 2 — there are plenty of resources out there about test.check
.
Thanks @seancorfield you would need to use coercions to coerce strings in API requests (from json) to clojure types, correct? Thanks for the code on usage of s/explain-data. Will try it out
Depends on whether you’re dealing with JSON or plain URL/form parameters but, yes, mostly you need something to coerce strings to numbers, booleans, dates, etc — and then conform with Spec.
sure, makes sense
Thanks!
So, next question: I have two files-- File A has 24435 unique sorted lines and File B has unique sorted 22391 lines. I’m reading the lines of each file into a sequence using the method I asked about previously so that I can get a difference between the two with (count (set/difference A_set B_set))
. However, when I do that, I get “22391” which I’m certain isn’t correct. Is there a better way to get the difference between the two files?
http://clojure.github.io/clojure/clojure.data-api.html#clojure.data/diff may help
since they're sorted it'd probably be worth to take, say, the first 10 lines of each file to get a better understanding of your problem. this problem feels like a "fix one small thing and the rest will settle out"
@ddouglass I guess I’m going to have to. Something is really throwing this off. I just tried @alexmiller’s suggestion and that gives me a count of “3”, which isn’t right either. I’m expecting a few hundred.
using def
inside other code is very rarely a good idea
@prabu.rajan This works as expected for me. Did you perhaps redefine the function and then forget to re-run the instrument
code?
Here’s what I get:
{:path []
:pred (clojure.core/fn
[%]
(clojure.core/contains?
(:menu
%)
(clojure.core/keyword
(helloworld.restaurant/slugify
(:helloworld.restaurant/name
(:order-item
%))))))
:val {:menu {:kothu-parotta {:helloworld.restaurant/name "Kothu Parotta"
:helloworld.restaurant/price 9.5
:helloworld.restaurant/quantity 50}
:chicken-biriyani ...
(when I request something that is not on the menu)
I think your extract-host-project
function could be a little simpler with the power of destructuring
Thanks @seancorfield, for some reason the same code works fine for me now. I added the other validation too now in the fdef (to validate the order quantity should be lesser than the quantity in the inventory) and it works fine. I am experiencing the awesomeness of clojure and spec!
(s/fdef update-menu-item-for-order-item
:args (s/and (s/cat :menu ::menu :order-item ::order-item)
#(contains? (:menu %) (keyword (slugify (::name (:order-item %)))))
#(< (::quantity (:order-item %))
(->> (:menu %)
((keyword (slugify (::name (:order-item %)))))
(::quantity))))
:ret ::menu)
With the function spec in place, I simplified my function’s logic to remove all nil and inventory checks and throwing exceptions and all that stuff like below
(defn update-menu-item-for-order-item [menu order-item]
(let [item-id (keyword (slugify (::name order-item)))
order-quantity (::quantity order-item)]
(update-in menu
[item-id]
#(assoc % ::quantity (- (::quantity %) order-quantity)))))
Just bear in mind that you would normally only use instrument
in dev/test, not in production so you need to think about what should happen if your code violates that contract in production with no instrumentation in place.
So, should I still have the original checks in my code so that it gets validated in production? What is the recommended approach? Maybe is it ok that only external facing API functions have such checks in production and all internal functions use spec instrumentations at dev/test?
Turns out it was a Byte Order Mark hiding out at the beginning of one of the files.
It depends.
If this is something that you would previously — before Clojure, before Spec — written as explicit checks in production with either error handling or exceptions, then that’s how you should write it now, in Clojure — with Spec if you want to leverage its ability to validate data.
If you only want these checks in place while you develop & test your code, instrument
is a reasonable way to approach it.
I can’t remember if I’ve linked you to this before? https://corfield.org/blog/2019/09/13/using-spec/
If you watch some of the talks about Spec, you’ll see there’s often a recommendation to use Spec around system boundaries and not for internal functions, on the grounds that it is not a type system.
But while you are developing functions that manipulate complex data (or have complex relationships encoded in the data) using Spec can be very valuable to help you get the functions correct, both via instrumentation and via generative testing (such as stest/check
).
Classic :)