All I'm trying to do is create an auto-generated UUID for clojure defrecord's when they are created. I've tried the following:
(ns myns
(:require [clj-uuid :as uuid])
(defrecord Thing [thing-id name])
(defn create-thing
[name]
(map->Thing {:thing-id (uuid/v1)
:name name}))
Followed by:
(repeat 5 (create-thing "bob"))
But I get the same UUID created for every Thing I create. Help would be appreciated!
I'm suspicious about using a dedicated lib for this, given how easy it is to do via interop using the built in UUID class that comes with the jvm.
(ns myns
(:import (java.util UUID)))
(defrecord Thing [thing-id name])
(defn create-thing
[name]
(map->Thing {:thing-id (UUID/randomUUID)
:name name}))
;; using repeatedly instead of repeat generates new values,
;; instead of reusing the initial value
(repeatedly 5 #(create-thing "bob"))
Related
I made service endpoint api for getting single object by id and it works as expected. I tested it with Postman and in handler function. I use cljs-ajax library for asynchronous client. I cant change the state of Reagent atom when I get response. Here is the code:
(ns businesspartners.core
(:require [reagent.core :as r]
[ajax.core :refer [GET POST]]
[clojure.string :as string]))
(def business-partner (r/atom nil))
(defn get-partner-by-id [id]
(GET "/api/get-partner-by-id"
{:headers {"Accept" "application/transit+json"}
:params {:id id}
:handler #(reset! business-partner (:business-partner %))}))
When I tried to access business-partner atom I got nil value for that atom. I can't figure out why because another method is almost the same except it get's list of business partners and works fine.
When I change the get-partner-by-id function:
(defn get-partner-by-id [id]
(GET "/api/get-partner-by-id"
{:headers {"Accept" "application/transit+json"}
:params {:id id}
:handler (fn [arg]
(println :handler-arg arg)
(reset! business-partner (:business-partner arg))
(println "Business partner from handler: " #business-partner))}))
Output in the browser console:
:handler-arg {:_id 5e7ad2c84b5c2d44583e8ecd,
:address Main Street,
:email nenmit#gmail.com,
:phone 555888,
:name Nen Mit}
Business partner from handler: nil
So, as you can see, I have my object in handler as desired, but when I try to reset my atom nothing happens. That's the core of the problem I think. Thank you Alan.
When in doubt, use debug print statements. Make your handler look like this:
:handler (fn [arg]
(println :handler-arg arg)
(reset! business-partner (:business-partner arg)))
You may also want to use clojure.pprint/pprint to pretty-print the output, or also add (type arg) to the output.
You may also want to initialize the atom to a specific value like
:bp-default so you can see if the nil you observe is the original one or if it is being reset to nil.
Update
So it is clear the key :business-partner does not exist in the map you are receiveing. This is what you must debug.
Trying to pull a non-existent key out of a map always returns nil. You could also use the 3-arg version of get to make this explicit. Convert
(:business-partner arg) => (get arg :business-partner ::not-found)
and you'll see the keyword ::not-found appear in your atom, verifying what is occurring.
In order to catch these problems early, I nearly always use a simple function grab from the Tupelo library like so:
(:business-partner arg) => (grab :business-partner arg)
The grab function will throw an exception if the expected key is not found. This provides early-warning of problems so you can track them down faster.
Another hint: next time use prn instead of println and it will retain double-quotes on string output like:
"Main Street"
I would like a macro this-ns such that it returns the namespace of the location where it is being called. For instance, if I have this code
(ns nstest.main
(:require [nstest.core :as nstest]))
(defn ns-str [x]
(-> x (.getName) name))
(defn -main [& args]
(println "The ns according to *ns*:" (ns-str *ns*))
(println "The actual ns:" (ns-str (nstest/this-ns))))
I would expect that calling lein run would produce this output:
The ns according to *ns*: user
The actual ns: nstest.main
What I came up with as implementation was the following code:
(ns nstest.core)
(defmacro this-ns []
(let [s (gensym)]
`(do (def ~s)
(-> (var ~s)
(.ns)))))
It does seem to work, but it feels very hacky. Notably, in the above example it will expand to def being invoked inside the -main function which does not feel very clean.
My question: Is there a better way to implement this-ns to obtain the namespace where this-ns is called?
here is one more variant:
(defmacro this-ns []
`(->> (fn []) str (re-find #"^.*?(?=\$|$)") symbol find-ns))
the thing is the anonymous function is compiled to a class named something like
playground.core$_main$fn__181#27a0a5a2, so it starts with the name of the actual namespace the function gets compiled in.
Can't say it looks any less hacky, then your variant, still it avoids the side effect, introduced by def in your case.
Interesting question. I would never have guessed that your code would output user for the first println statement.
The problem is that only the Clojure compiler knows the name of an NS, and that is only when a source file is being compiled. This information is lost before any functions in the NS are called at runtime. That is why we get user from the code: apparently lein calls demo.core/-main from the user ns.
The only way to save the NS information so it is accessible at runtime (vs compile time) is to force an addition to the NS under a known name, as you did with your def in the macro. This is similar to Sean's trick (from Carcingenicate's link):
(def ^:private my-ns *ns*) ; need to paste this into *each* ns
The only other approach I could think of was to somehow get the Java call stack, so we could find out who called our "get-ns" function. Of course, Java provides a simple way to examine the call stack:
(ns demo.core
(:use tupelo.core)
(:require
[clojure.string :as str]))
(defn caller-ns-func []
(let [ex (RuntimeException. "dummy")
st (.getStackTrace ex)
class-names (mapv #(.getClassName %) st)
class-name-this (first class-names)
class-name-caller (first
(drop-while #(= class-name-this %)
class-names))
; class-name-caller is like "tst.demo.core$funky"
[ns-name fn-name] (str/split class-name-caller #"\$")]
(vals->map ns-name fn-name)))
and usage:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[demo.core :as core]))
(defn funky [& args]
(spyx (core/caller-ns-func)))
(dotest
(funky))
with result:
(core/caller-ns-func) => {:ns-name "tst.demo.core", :fn-name "funky"}
And we didn't even need a macro!
Although the below example seems a bit strange, it's because I'm trying to reduce a fairly large problem I've got at present to a minimal example. I'm struggling to work out how to call into multimethods when they're sitting behind a couple of abstraction layers and the defmulti and corresponding defmethods are defined in multiple namespaces. I really feel like I'm missing something obvious here...
Suppose I've got the following scenario:
I purchase stuff from a variety of suppliers, via their own proprietary interfaces
I want to implement a common interface to talk to each of those suppliers
I want to be able to purchase different items from different suppliers
Using Clojure, the recommended ways of implementing a common interface would be via protocols or multimethods. In this case, as I'm switching based on the value of the supplier, I think the best way to handle the situation I'm describing below is via multimethods (but I could be wrong).
My multimethod definitions would look something like this, which defines a common interface I want to use to talk to every supplier's APIs:
(ns myapp.suppliers.interface)
(defmulti purchase-item :supplier)
(defmulti get-item-price :supplier)
For each supplier, I probably want something like:
(ns myapp.suppliers.supplier1
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod purchase-item :supplier1 [item quantity] ...)
(defmethod get-item-price :supplier1 [item] ...)
and
(ns myapp.suppliers.supplier2
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod purchase-item :supplier2 [item quantity] ...)
(defmethod get-item-price :supplier2 [item] ...)
So far, no problem
Now to my code which calls these abstracted methods, which I assume looks something like:
(ns myapp.suppliers.api
(:require [myapp.suppliers.supplier1 :as supplier1]
[myapp.suppliers.supplier2 :as supplier2])
(defn buy-something
[supplier item quantity]
(purchase-item [supplier item quantity])
(defn price-something
[supplier item]
(get-item-price [supplier item])
This is starting to look a bit ... ugly. Every time I implement a new supplier's API, I'll need to change myapp.suppliers.api to :require that new supplier's methods and recompile.
Now I'm working at the next level up, and I want to buy a widget from supplier2.
(ns myapp.core
(:require [myapp.suppliers.api :as supplier])
(def buy-widget-from-supplier2
(buy-something :supplier2 widget 1)
This can't work, because :supplier2 hasn't been defined anywhere in this namespace.
Is there a more elegant way to write this code? In particular, in myapp.core, how can I buy-something from :supplier2?
Initial notes
It's hard to tell if you mixed up some things in the process of simplifying the example, or if they weren't quite right out of the gate. For an example of what I'm referring to, consider purchase-item, though the issues are similar for get-item-price:
The defmulti call is a single-argument function
The defmethod calls each take two arguments
The call in buy-something passes a vector to purchase-item, but looking up the :supplier keyword in a vector will always return nil
Your concerns
Every time I implement a new supplier's API, I'll need to change myapp.suppliers.api to :require that new supplier's methods and recompile.
If you require the myapp.suppliers.interface namespace myapp.suppliers.api, the problem can be avoided
This can't work, because :supplier2 hasn't been defined anywhere in this namespace.
Simply put, this will work. :)
Is there a more elegant way to write this code? In particular, in myapp.core, how can I buy-something from :supplier2?
Certainly, but this solution is going to make some assumption based on the ambiguities in the Initial notes.
Without straying too far from your original design, here's a fully-working example of how I interpret what you were trying to achieve:
myapp.suppliers.interface
(ns myapp.suppliers.interface)
(defmulti purchase-item (fn [supplier item quantity] supplier))
myapp.suppliers.supplier1
(ns myapp.suppliers.supplier1
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod supplier-api/purchase-item :supplier1 [supplier item quantity]
(format "Purchasing %dx %s from %s" quantity (str item) (str supplier)))
myapp.suppliers.supplier2
(ns myapp.suppliers.supplier2
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod supplier-api/purchase-item :supplier2 [supplier item quantity]
(format "Purchasing %dx %s from %s" quantity (str item) (str supplier)))
myapp.suppliers.api
(ns myapp.suppliers.api
(:require [myapp.suppliers.interface :as interface]))
(defn buy-something [supplier item quantity]
(interface/purchase-item supplier item quantity))
myapp.core
(ns myapp.core
(:require [myapp.suppliers.api :as supplier]))
(def widget {:id 1234 :name "Monchkin"})
(supplier/buy-something :supplier1 widget 15)
;;=> "Purchasing 15x {:id 1234, :name \"Monchkin\"} from :supplier1"
(supplier/buy-something :supplier2 widget 3)
;;=> "Purchasing 3x {:id 1234, :name \"Monchkin\"} from :supplier2"
As you can see, the supplier/buy-something calls propagate to the appropriate multimethod implementations. Hopefully this helps you get where you were trying to go.
I have a very simple question about using Prismatic/schema to validate functions. I have a schema for a map that has a single key, the value of which is a function that takes a Bar schema as its single argument and returns anything (used for side effects):
(require '[schema.core :as s])
(def Bar {:baz s/Int})
(def Action :???)
(def Foo {:action Action})
The question is, how do I define Action? I've tried this:
(require '[schema.macros :as sm])
(def Action (sm/=> s/Any Bar))
This looks promising, but I can't get it to fail validation:
(s/explain Action)
;=> (=> Any {:baz Int})
;; This should fail
(s/validate Foo {:action :anything-goes})
;=> {:action :anything-goes}
What am I doing wrong here?
I read the docs and the tests in core_test, but I can't figure out how to do this.
I've found this: https://github.com/Prismatic/schema/blob/a21cc0113ed497f6410c55d92d9088bd710f0b47/src/cljx/schema/core.cljx#L888
So it would be something like:
(def Action (s/make-fn-schema s/Any [[Bar]]))
Although, the documentation does say this:
Currently function schemas are purely descriptive; they validate against any function, regardless of actual input and output types
I defined the following MyCache.clj
(ns abcd.MyCache
(:gen-class
:name "abcd.MyCache"
:init "init"
:constructors { [java.text.DateFormat][] }
:methods [ [now [] void] [myformat [long] String] ]
:state "state"
:main false))
(defn -init[format]
([[] (atom {:format format})]))
(defn -now[this] ( (:format #(.state this)) (System/currentTimeMillis)))
(defn -myformat[this time]
( (:format #(.state this) (new java.util.Date time))))
I compiled the above file using (compile 'abcd.MyCache) successfully.
When I am trying to use the generated classes as shown below..I am getting errors. Please help.
user=> (new abcd.MyCache (new java.text.SimpleDateFormat "mmDDyyyy"))
IllegalArgumentException Key must be integer clojure.lang.APersistentVector.invoke (APersistentVector.java:265)
I don't feel well about this:
(defn -init[format]
([] [atom {:format format}]))
You are trying to get an element from a vector and it is expects an index (number).
What is correct is to deref the atom and get its value as the index of the vector. But again in your case, you are trying to query an empty vector.
Notice also, that [atom {:format format}] isn't the correct way to create an atom. You should use:
(atom {:format format})
And by the way, the following form is the preferred one to create Java objects (nothing wrong with (new) of course):
(Date.)
(DateFormat.)