How can I make an IDerefable in clojurescript? - clojure

Is there a way to make another container in cljs like it is in clojure by implementing IDeref?
(reify clojure.lang.IDeref
(deref [_] ...))
The compiler warns IDeref is not a protocol

Try this:
(def a
(reify IDeref
(-deref [_] "Hello!")))
(.log js/console #a)
outputs "Hello!". You may want to use deftype:
(deftype LikeAtom []
IDeref
(-deref [_] "Hello!"))
(.log js/console #(LikeAtom.))

Related

Render resource when PUT

I am using liberator to build a API using Clojure. Given the follow code:
(defresource single-customer [id]
:allowed-methods [:get, :put]
:exists? (fn [_]
(let [e (get #cust/customers (keyword id))]
(if-not (nil? e)
{::entry e})))
:existed? (fn [_] (nil? (get #cust/customers (keyword id) ::sentinel)))
:available-media-types ["application/json"]
:can-put-to-missing? false
:put! (fn [q] (cust/set-as-fraudulent id))
:handle-ok ::entry)
Someone when can tell me if is possible, like the GET request, when I send a PUT request it be redirected to the resource ? "/customer/1" (for example) ?
Looking at the liberator decision graph, :put! can lead to either:
:handle-created (if :new?)
:handle-no-content (if not :new? and not :respond-with-entity?)
:handle-ok (if not :new, but :respond-with-entity?)
Try implementing :put! so it stores the entity as ::entry, and :handle-created similar to your current :handle-ok.

Using with-redefs inside a doseq

I have a Ring handler which uses several functions to build the response. If any of these functions throw an exception, it should be caught so a custom response body can be written before returning a 500.
I'm writing the unit test for the handler, and I want to ensure that an exception thrown by any of these functions will be handled as described above. My instinct was to use with-redefs inside a doseq:
(doseq [f [ns1/fn1 ns1/fn2 ns2/fn1]]
(with-redefs [f (fn [& args] (throw (RuntimeException. "fail!"))]
(let [resp (app (request :get "/foo")))))]
(is (= (:status resp) 500))
(is (= (:body resp) "Something went wrong")))))
Of course, given that with-redefs wants to change the root bindings of vars, it is treating f as a symbol. Is there any way to get it to rebind the var referred to by f? I suspect that I'm going to need a macro to accomplish this, but I'm hoping that someone can come up with a clever solution.
with-redefs is just sugar over repeated calls to alter-var-root, so you can just write the desugared form yourself, something like:
(doseq [v [#'ns1/fn1 #'ns1/fn2 #'ns2/fn1]]
(let [old #v]
(try
(alter-var-root v (constantly (fn [& args]
(throw (RuntimeException. "fail!")))))
(let [resp (app (request :get "/foo")))
(is (= (:status resp) 500))
(is (= (:body resp) "Something went wrong")))
(finally (alter-var-root v (constantly old))))))
Following on from amalloy's great answer, here's a utility function that I wrote to use in my tests:
(defn with-mocks
"Iterates through a list of functions to-mock, rebinding each to mock-fn, and calls
test-fn with the optional test-args.
Example:
(defn foobar [a b]
(try (+ (foo a) (bar b))
(catch Exception _ 1)))
(deftest test-foobar
(testing \"Exceptions are handled\"
(with-mocks
[#'foo #'bar]
(fn [& _] (throw (RuntimeException. \"\")))
(fn [a b] (is (= 1 (foobar a b)))) 1 2)))"
[to-mock mock-fn test-fn & test-args]
(doseq [f to-mock]
(let [real-fn #f]
(try
(alter-var-root f (constantly mock-fn))
(apply test-fn test-args)
(finally
(alter-var-root f (constantly real-fn)))))))

how to separate concerns in the below fn

The function below does 2 things -
Checks if the atom is nil or fetch-agin is true, and then fetches the data.
It processes the data by calling (add-date-strings).
What is a better pattern to separate out the above two concerns ?
(def retrieved-data (atom nil))
(defn fetch-it!
[fetch-again?]
(if (or fetch-again?
(nil? #retrieved-data))
(->> (exec-services)
(map #(add-date-strings (:time %)))
(reset! retrieved-data))
#retrieved-data))
One possible refactoring would be:
(def retrieved-data (atom nil))
(defn fetch []
(->> (exec-services)
(map #(add-date-strings (:time %)))))
(defn fetch-it!
([]
(fetch-it! false))
([force]
(if (or force (nil? #retrieved-data))
(reset! retrieved-data (fetch))
#retrieved-data)))
By the way, the pattern to seperate out concerns is called "functions" :)
To really separate the concerns I think it might be better to define a separate fetch and process function. So that in no way they are complected.
(def retrieved-data (atom nil))
(defn fetcher []
(->> (exec-services)
(map #(add-date-strings (:time %)))))
(defn fetch-again? [force]
(fn [data] (or force (nil? data))))
(defn fetch-it! [fetch-fn data fetch-again?]
(when (fetch-again? #data))
(reset! data (fetch-fn))))
;;Usage
(fetch-it! fetcher retrieved-data (fetch-again? true))
Notice that I also gave the data atom as an argument.

How to do hooks in Clojure

I have a situation where I am creating and destroying objects in one clojure namespace, and want another namespace to co-ordinate. However I do not want the first namespace to have to call the second explicitly on object destruction.
In Java, I could use a listener. Unfortunately the underlying java libraries do not signal events on object destruction. If I were in Emacs-Lisp, then I'd use hooks which do the trick.
Now, in clojure I am not so sure. I have found the Robert Hooke library https://github.com/technomancy/robert-hooke. But this is more like defadvice in elisp terms -- I am composing functions. More over the documentation says:
"Hooks are meant to extend functions you don't control; if you own the target function there are obviously better ways to change its behaviour."
Sadly, I am not finding it so obvious.
Another possibility would be to use add-watch, but this is marked as alpha.
Am I missing another obvious solution?
Example Added:
So First namespace....
(ns scratch-clj.first
(:require [scratch-clj.another]))
(def listf (ref ()))
(defn add-object []
(dosync
(ref-set listf (conj
#listf (Object.))))
(println listf))
(defn remove-object []
(scratch-clj.another/do-something-useful (first #listf))
(dosync
(ref-set listf (rest #listf)))
(println listf))
(add-object)
(remove-object)
Second namespace
(ns scratch-clj.another)
(defn do-something-useful [object]
(println "object removed is:" object))
The problem here is that scratch-clj.first has to require another and explicitly push removal events across. This is a bit clunky, but also doesn't work if I had "yet-another" namespace, which also wanted to listen.
Hence I thought of hooking the first function.
Is this solution suitable to your requirements?
scratch-clj.first:
(ns scratch-clj.first)
(def listf (atom []))
(def destroy-listeners (atom []))
(def add-listeners (atom []))
(defn add-destroy-listener [f]
(swap! destroy-listeners conj f))
(defn add-add-listener [f]
(swap! add-listeners conj f))
(defn add-object []
(let [o (Object.)]
(doseq [f #add-listeners] (f o))
(swap! listf conj o)
(println #listf)))
(defn remove-object []
(doseq [f #destroy-listeners] (f (first #listf)))
(swap! listf rest)
(println #listf))
Some listeners:
(ns scratch-clj.another
(:require [scratch-clj.first :as fst]))
(defn do-something-useful-on-remove [object]
(println "object removed is:" object))
(defn do-something-useful-on-add [object]
(println "object added is:" object))
Init binds:
(ns scratch-clj.testit
(require [scratch-clj.another :as another]
[scratch-clj.first :as fst]))
(defn add-listeners []
(fst/add-destroy-listener another/do-something-useful-on-remove)
(fst/add-add-listener another/do-something-useful-on-add))
(defn test-it []
(add-listeners)
(fst/add-object)
(fst/remove-object))
test:
(test-it)
=> object added is: #<Object java.lang.Object#c7aaef>
[#<Object java.lang.Object#c7aaef>]
object removed is: #<Object java.lang.Object#c7aaef>
()
It sounds a lot like what you're describing is callbacks.
Something like:
(defn make-object
[destructor-fn]
{:destructor destructor-fn :other-data "data"})
(defn destroy-object
[obj]
((:destructor obj) obj))
; somewhere at the calling code...
user> (defn my-callback [o] (pr [:destroying o]))
#'user/my-callback
user> (destroy-object (make-object my-callback))
[:destroying {:destructor #<user$my_callback user$my_callback#73b8cdd5>, :other-data "data"}]
nil
user>
So, here is my final solution following mobytes suggestion. A bit more work, but
I suspect that I will want this in future.
Thanks for all the help
;; hook system
(defn make-hook []
(atom []))
(defn add-hook [hook func]
(do
(when-not
(some #{func} #hook)
(swap! hook conj func))
#hook))
(defn remove-hook [hook func]
(swap! hook
(partial
remove #{func})))
(defn clear-hook [hook]
(reset! hook []))
(defn run-hook
([hook]
(doseq [func #hook] (func)))
([hook & rest]
(doseq [func #hook] (apply func rest))))
(defn phils-hook []
(println "Phils hook"))
(defn phils-hook2 []
(println "Phils hook2"))
(def test-hook (make-hook))
(add-hook test-hook phils-hook)
(add-hook test-hook phils-hook2)
(run-hook test-hook)
(remove-hook test-hook phils-hook)
(run-hook test-hook)

swap! atom and then: java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn

So, in my core.clj file I have:
(def page-buffer (BufferedReader. (InputStreamReader. (clojure.java.io/input-stream (clojure.java.io/resource "mitochondria.html")))))
(def parsed-page (atom ""))
and then later:
(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(swap! parsed-page (with-open []
(.toString (reduce #(.append %1 %2)
(StringBuffer.) (line-seq page-buffer)))))
(println "Server is starting")
(println "port: " port)
(run-server port)))
This compiles and then I turn it into an uberjar. But when I run it I get the line with swap! blows up:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn
at clojure.core$swap_BANG_.invoke(core.clj:2106)
at serve_pages_from_memory.core$_main.doInvoke(core.clj:29)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at serve_pages_from_memory.core.main(Unknown Source)
I tried this without using an atom and everything worked just fine (using a var defined with "def" to be a string) but eventually I want to send this var to multiple threads, so I need for this to be an atom or agent.
What have I done wrong?
UPDATE:
Jeremy Heiler, thanks. Fixed, but the result is ugly as sin:
(defn parse-buffer [& everything-else]
(with-open []
(.toString (reduce #(.append %1 %2)
(StringBuffer.) (line-seq page-buffer)))))
(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(swap! parsed-page parse-buffer)
(println "Server is starting")
(println "port: " port)
(run-server port)))
I have to give an argument to parse-buffer or I get an error. But I do not use the argument, so this is ugly. I must be writing this wrong, yes?
You need to pass a function to swap!. Right now you are passing it a value.
(let [foo (atom 1)]
(swap! foo + 2)
#foo)
The above expression will return 3. The passed in function takes the current value of the atom, and the return value becomes the new value of the atom. Any extra arguments are passed to the given function.
To comment on your update: Yes, you do need to give an argument to the function used in swap!. The first argument is the current value of the the atom. However, you don't need to hack parse-buffer in order to make it work. You can just wrap it with an anonymous function.
Also, parse-buffer can be simplified greatly. Since line-seq returns a sequence of lines from the given reader, and str uses a StringBuilder internally, you can just apply it over the sequence.
(defn parse-buffer []
(with-open [buf page-buffer]
(apply str (line-seq buf))))
(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(swap! parsed-page (fn [cur-val] (parse-buffer)))
(println "Server is starting")
(println "port: " port)
(run-server port)))