I am using enfocus for dom manipulation in ClojuseScript.
I have an event handler:
(ns numeros-linguas.script
(:require [enfocus.core :as ef]
[enfocus.events :as ev])
(:require-macros [enfocus.macros :as em]))
(defn aviso-seleciona []
(ef/at "select" (ev/listen :change
(fn [evt]
(let [lingua-id (ef/from [:select] (ef/get-prop :value))
lingua-selector (str "#" lingua-id)
cores (array "azul" "vermelho" "laranja" "verde")
linguas-visiveis (when-let [r (ef/from [:#resultado :> :div.visivel] (ef/get-attr :id))]
(-> r
list
flatten))]
(ef/at "#resultado" (ef/append (ef/from lingua-selector identity)))
(ef/at lingua-selector (ef/remove-class "invisivel"))
(ef/at lingua-selector (ef/add-class "visivel"))
(map #(do
(ef/at (str "#" %)
(ef/add-class %2))
(map (fn [cor]
(ef/at (str "#" %)
(ef/remove-class cor)))
(remove #{%2} cores)))
linguas-visiveis cores))))))
(set! (.-onload js/window)
aviso-seleciona)
The tricky part is in the map. I want to remove some css classes from some div elements and add some others at these elements. But it is not working. All the other elements in the form are working except this one. But if I copy and paste the form to a repl connected to that page and run the code it works as expected. I tryed to comment parts of the code to understand what is going on but with no hope.
I've noticed you are doing side-effects in map, which is not going to work because map is lazy, i.e. it won't evaluate unless you try to use sequence it produces, which explains why it works in the REPL, but not in the browser.
For quickest and dirtiest fix, try using dorun to force evaluation of your sequence: (dorun (map #(do ...... ).
or, idiomatically, rewrite your code to something resembling this (which is not very idiomatic):
(doseq [[el cor-to-add] (map vector linguas-visiveis cores)]
(ef/at (str "#" el)
(ef/add-class cor-to-add))
(doseq [cor-to-remove (remove #{cor} cores)]
(ef/at (str "#" el)
(ef/remove-class cor-to-remove)))
)
or this, removing all classes from every element, first:
(doseq [el linguas-visiveis
cor cores]
(ef/at (str "#" el)
(ef/remove-class cor)))
and adding only required classes later:
(doseq [[el cor] (map vector linguas-visiveis cores)]
(ef/at (str "#" el)
(ef/add-class cor)))
Related
I have a function that reads a file using js/FileReader.:
(defn read-file [file]
(let [js-file-reader (js/FileReader.)]
(set! (.-onload js-file-reader)
(fn [evt]
(let [result (-> evt .-target .-result)
array (js/Uint8Array. result)]
{:content array}))) ; <- This is the value that 'read-file' should return
(.readAsArrayBuffer js-file-reader file)))
The problem is that I would like it to return the value of the .-onload method of the FileReader, but I only get (of course) the value of (.readAsArrayBuffer js-file-reader file) which, naturally, is undefined.
Thank you very much!
Edit
After trying with Martin Půda's answer, I think that the problem has to do with an asyncrhonous thing. I tested this code:
(defn read-file [file]
(let [js-file-reader (js/FileReader.)
reading-result (atom)
done? (atom false)]
(set! (.-onload js-file-reader)
(fn [evt]
(let [result (-> evt .-target .-result)
array (js/Uint8Array. result)]
(reset! reading-result {:content array})
(reset! done? true)
(js/console.log "in: " (:content #reading-result)))))
(.readAsArrayBuffer js-file-reader file)
;; (while (not #done?) (js/console.log (.-readyState js-file-reader)))
(js/console.log "out: " #reading-result)
#reading-result))
I get first the log of out: undefined , and then the log of in: (with the desired result).
When I uncomment the line (while...), I get an infinite loop of 1's... So I think that the function never notices that the FileReader was done... I don't know how to solve this...
Try this:
(defn read-file [file]
(let [js-file-reader (js/FileReader.)
reading-result (atom)]
(set! (.-onload js-file-reader)
(fn [evt]
(let [result (-> evt .-target .-result)
array (js/Uint8Array. result)]
(reset! reading-result {:content array}))))
(.readAsArrayBuffer js-file-reader file)
#reading-result))
See docs for atom and reset!.
EDIT: See HTML5 FileReader how to return result?- yes, it's asynchronous. I see two possibilities:
You will write all function calls, which work with returned value, inside event listeners.
You will follow this CLJS guide for Promises and use JS Promises or clojure.core.async.
After much reading, I solved it using a callback function:
(ns lopezsolerluis.fits)
(defn read-file [file callback]
(let [js-file-reader (js/FileReader.)]
(set! (.-onload js-file-reader)
(fn [evt]
(let [result (-> evt .-target .-result)
array (js/Uint8Array. result)]
(callback array))))
(.readAsArrayBuffer js-file-reader file)))
Then read-file is called from this:
(ns lopezsolerluis.annie-web)
(defn procesar-archivo [result]
(js/console.log "Bloques: " result))
(defn input-file []
[:input {:type "file" :id "fits" :name "imagenFits" :accept "image/fits"
:on-change (fn [this]
(if (not (= "" (-> this .-target .-value)))
(let [^js/File file (-> this .-target .-files (aget 0))]
(fits/read-file file procesar-archivo)))
(set! (-> this .-target .-value) ""))}])
I added the namespaces because it surprised me that the callback machinery worked even across namespaces. Well, maybe it shouldn't surprise me; but I am learning and was a new concept for me. :)
I answer my question in case it's useful for others (it costed me a lot! :))
Probably the title is not 100% correct, but let me show you the issue:
(defmacro nvp!
[n width height]
`(q/defsketch (symbol (str (name '~n) "-viewport" ))
:title (name '~n))
In short: there's a macro called defsketch (it's part of the lib quil, but that does not matter). It's signature is basically defsketch [applet-name & options], and it creates something and binds it to a var called applet-name. For some reason, I want to wrap this into another macro that -- among other things -- takes an applet-name parameter, extends that name into applet-name-viewport and passes that to defsketch. I, however, am unable to figure out how to do that correctly (using macroexpand on the code above ((pprint (macroexpand(nvp test-name 500 500))), I get
(def(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport"))
(quil.applet/applet
:title (clojure.core/name 'my-namespace.core/test-name)))
(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport") -- this part looks good, but it should be evaluated somehow before passing it to the inner macro...
You need to unquote the form that generates the new symbol instead of the original symbol itself.
Here is a small example of how to accomplish this using a defn like macro for the inner macro:
(defmacro mydefn
[name & body]
`(defn ~name ~#body))
(defmacro defnview
[n & body]
`(mydefn ~(symbol (str (name n) "-viewport")) ~#body))
;; OR
(defmacro defnview
[n & body]
(let [n (symbol (str (name n) "-viewport"))]
`(mydefn ~n ~#body)))
Example:
(defnview foo [a] a)
;; => #'user/foo-viewport
(foo-viewport 1)
;; => 1
(macroexpand '(defnview foo [a] a))
;; => (def foo-viewport (clojure.core/fn ([a] a)))
In Clojure, how do I make a library macro which processes supplied functions metadata and return some result? Amount of functions is unlimited and they should be passed without being boxed into a sequence ((my-macro fn1 fn2) instead of (my-macro [fn1 fn2]))
Say, we expect function vars having :foo keys in meta and the macro concatenates their values. The following snippet should work in REPL (considering my-macro is in the namespace):
user=> (defn my-func-1 {:foo "bar"} [])
(defn my-func-1 {:foo "bar"} [])
#'user/my-func-1
user=> (defn my-func-2 {:foo "baz"} [])
(defn my-func-2 {:foo "baz"} [])
#'user/my-func-2
user=> (my-macro my-func-1 my-func2)
(my-macro my-func-1 my-func2)
"barbaz"
I tried several approaches but was only able to process single function so far.
Thanks!
Try this:
(defmacro my-macro [& fns]
`(clojure.string/join (list ~#(map (fn [x] `(:foo (meta (var ~x)))) fns))))
(defn ^{:foo "bar"} my-func-1 [])
(defn ^{:foo "baz"} my-func-2 [])
(my-macro my-func-1 my-func-2) ;; => "barbaz"
How it Works
If you expand the macro you can start to see the parts in play.
(macroexpand '(my-macro my-func-1 my-func-2))
(clojure.string/join
(clojure.core/list (:foo (clojure.core/meta (var my-func-1)))
(:foo (clojure.core/meta (var my-func-2)))))
(var my-func-1)
Function metadata is stored on the var, so using (meta my-func-1) is not sufficient. But, var is a special form and does not compose like a normal function.
(fn [x] `(:foo (meta (var ~x))))
This anonymous function exists inside an escaped form, so it is processed inside the macro to produce the output forms. Internally it will create a the (:foo (meta (var my-func-1))) form by first backtick escaping the outer form to declare it a literal, and not evaluated, list and then unescaping the x var with a tilde to output the value instead of the symbol.
`(clojure.string/join (list ~#(map (fn [x] `(:foo (meta (var ~x))))
fns)))
This entire form is backtick escaped, so it will be returned literally. But, I still need to evaluate the map function generating the (:foo (meta (var my-func-1))) form. In this case I have unescaped, and spliced (#) the result of, the map form directly. This first evaluates the map function and returns a list of generated forms, and then takes the contents of that list and splices it into the parent list.
(defmacro test1 [x] `(~x))
(defmacro test2 [x] `(~#x))
(macroexpand '(test1 (1 2 3))) ;; => ((1 2 3))
(macroexpand '(test2 (1 2 3))) ;; => (1 2 3)
You could also split out the map function in a let statement before hand for slightly more readability.
(defmacro my-macro [& fns]
(let [metacalls (map (fn [x] `(:foo (meta (var ~x)))) fns)]
`(clojure.string/join (list ~#metacalls))))
I have an enum in postgresql defined like so:
create type color as enum ('yellow', 'purple', 'white', 'black');
And I can get to the Jdbc4Array like so:
(def colors
((first (sql/with-connection db/db
(sql/with-query-results res
["select enum_range(null::color)"]
(doall res)))) :enum_range))
This shows an object like this:
#<Jdbc4Array {yellow,purple,white,black}>
But trying the usual things throws an exception:
(.getArray colors) => stream closed
So I figure I need to access the array before the connection is closed:
(def colors
((sql/with-connection db/db
(sql/with-query-results res
["select enum_range(null::color)"]
(.getArray ((first (doall res)) :enum_range))))))
But in this case I get this exception:
Method org.postgresql.jdbc4.Jdbc4Array.getArrayImpl(long,int,Map)
is not yet implemented.
Sinister. What can I do here?
There is something very strange going on with the Postgresql Jdbc4Array.getArray() implementation, I couldn't get it to work. But, I have some success with .getResultSet() :
user=> (with-connection db (with-query-results rs ["select enum_range(null::color)"]
(.getResultSet (get (first(doall rs)) :enum_range))))
#<Jdbc4ResultSet org.postgresql.jdbc4.Jdbc4ResultSet#17cff66>
Now, array's contents could be accessed via standard ResultSet interface. I've copied some code from clojure.contrib.sql to do it:
(defn resultset-seq
[^java.sql.ResultSet rs]
(let [rsmeta (. rs (getMetaData))
idxs (range 1 (inc (. rsmeta (getColumnCount))))
keys (map (fn [i] (. rsmeta (getColumnLabel i))) idxs)
check-keys (or (apply distinct? keys)
(throw (Exception. "ResultSet must have unique column labels")))
row-struct (apply create-struct keys)
row-values (fn [] (map (fn [^Integer i] (. rs (getObject i))) idxs))
rows (fn thisfn []
(when (. rs (next))
(cons (apply struct row-struct (row-values)) (lazy-seq (thisfn)))))]
(rows)))
which gives (sorry for quick-hack style code)
user=> (with-connection db
(with-query-results rs ["select enum_range(null::color)"]
(get (first (resultset-seq
(.getResultSet (get (first(doall rs))
:enum_range))))
"VALUE")))
#<PGobject yellow>
I'm using the congomongo library in two processes. One of the processes interfaces with the databases without error. The other process is having more trouble. If I try to interact with the database from that processes slime repl, it too is has no problem, but if I try to interact through telnet things work badly.
Command Line:
joshua#joshua-Aspire-5251:/data/db$ telnet localhost 4576
Trying ::1...
Connected to localhost.localdomain.
Escape character is '^]'.
http://news.ycombinator.com/item?id=2286907
Connection closed by foreign host.
Server's Reaction
http://news.ycombinator.com/item?id=2286907
Processing link.
Validating link.
Scraping item.
Wikifying items.
Uploading items.
From the REPL:
(process-link "http://news.ycombinator.com/item?id=2286907")
Server's Reaction:
http://news.ycombinator.com/item?id=2286907
Processing link.
Validating link.
Scraping item.
Wikifying items.
Uploading items.
The only functional difference? One actually puts the items in the database and the other doesn't. Both seem to be accomplishing everything else.
(ns hnparser.main
(:use somnium.congomongo
[hnparser.core :only [scrape-item]]))
(mongo! :db "hacker-archives")
(defn valid-link?
[link]
(do
(println "Validating link.")
(not= nil (re-matches #"http://news\.ycombinator\.com/item\?id=\d+" link))))
(defn wikify [item]
(dissoc (assoc item :wiki [{:title (:title item)
:author (:user item)
:body (:body item)
:date (:date item)
:reason "Original post."}])
:title :user :body :date))
(defn wikify-items [items]
(do
(println "Wikifying items.")
(map wikify items)))
(defn upload-item
[item]
(if (nil? (fetch-one :items :where {:id (:id item)}))
(insert! :items (assoc item :scrape-date (java.util.Date.)))))
(defn upload-items
[items]
(do
(println "Uploading items.")
(map upload-item items)))
(defn process-link
[link]
(do
(println "Processing link.")
(if (valid-link? link)
(upload-items (wikify-items (scrape-item link))))))
(ns hnparser.server
(:use clojure.contrib.server-socket
hnparser.main)
(:import [java.io PushbackReader InputStreamReader]))
(defn read-links
[is os]
(let [in-reader (PushbackReader. (InputStreamReader. is))]
(let [input (str (read in-reader))]
(do
(println input)
(process-link input)))))
(defn -main
[]
(def *server* (create-server 4576 read-links)))
Note that this successfully scrapes (verified through print statements). It just doesn't upload to the database.
Map is lazy: you should never use map when you want side effects instead of return values. You can wrap a map with dorun, but in most cases it's easier to use doseq.
Also defn has an implicit do, so (fn [x] (do (println x) (inc x))) is the same as (fn [x] (println x) (inc x)).