In the following code, I have noticed the ^ character before what seems to be a map data structure. What is ^ used for, or what is ^{ used for?
(ns temper.core
(:gen-class)
(:require [mount.core :as mount]))
(mount/defstate ^{:on-reload :noop}
http-server
:start
(http/start
(-> env
(assoc :handler (lazy-run 'temper.handler 'app))
(update :port #(or (-> env :options :port) %))))
:stop
(http/stop http-server))
The ^ character is for metadata. Please see https://clojure.org/reference/reader#macrochars and https://clojure.org/reference/metadata.
^{:on-reload :noop} and (with-meta obj {:on-reload :noop}) are equivalent.
Related
Here is my code:
(ns cowl.server
(:use compojure.core)
(:require [ring.adapter.jetty :as jetty]
[ring.middleware.params :as params]
[ring.middleware.json :refer [wrap-json-response]]
[ring.util.response :refer [response]]
[clojure.data.json :as json]
[cowl.db :as db]))
(defroutes main-routes
(POST "/api/news/as-read" { body :body }
(str (json/read-str (slurp body))))))
(def app
(-> main-routes
wrap-json-response))
(defn serve []
(jetty/run-jetty app {:port 3000}))
If I post this JSON: { "name": "demas" } I get {"name" "demas"}. But this is not a Clojure map.
I need something like (:name (json/read-str (slurp body))). How can I get it ?
Instead of handling body JSON parsing by yourself, you can use ring.middleware.json/wrap-json-body. Just modify your middleware setup:
(def app
(-> main-routes
wrap-json-response
(wrap-json-body {:keywords? true})))
and your request :body will become JSON parsed to Clojure data.
You may wish to use the keywordize-keys function:
http://clojuredocs.org/clojure.walk/keywordize-keys
(ns xyz.core
(:require [clojure.walk :as walk]))
(walk/keywordize-keys {"a" 1 "b" 2})
;;=> {:a 1 :b 2}
You will probably also find that the Cheshire lib is the best way to process JSON in Clojure: https://github.com/dakrone/cheshire#decoding
;; parse some json
(parse-string "{\"foo\":\"bar\"}")
;; => {"foo" "bar"}
;; parse some json and get keywords back
(parse-string "{\"foo\":\"bar\"}" true) ; true -> want keyword keys
;; => {:foo "bar"}
;; parse some json and munge keywords with a custom function
(parse-string "{\"foo\":\"bar\"}" (fn [k] (keyword (.toUpperCase k))))
;; => {:FOO "bar"}
You can use :key-fn function as well:
(json/read-str (return :body)
:key-fn keyword)
Doing this you will parse your JSON to default map syntax.
Now can use compojure this way:
(GET ["/uri"] [para1 para2]
)
Para1 and para2 are all of type String.
I would like to let it know the type correcttly,like this:
(GET ["/uri"] [^String para1 ^Integer para2]
)
It can convert para1 to be Sting and para2 to Integer.
Is there some library or good way to do this?
This is possible as of Compojure 1.4.0 using the syntax [x :<< as-int]
This is not currently possible with only Compojure.
You could use Prismatic schema coercion.
(require '[schema.core :as s])
(require '[schema.coerce :as c])
(require '[compojure.core :refer :all])
(require '[ring.middleware.params :as rparams])
(def data {:para1 s/Str :para2 s/Int s/Any s/Any})
(def data-coercer (c/coercer data c/string-coercion-matcher ))
(def get-uri
(GET "/uri" r
(let [{:keys [para1 para2]} (data-coercer (:params r))]
(pr-str {:k1 para1 :k2 (inc para2)}))))
(def get-uri-wrapped
(let [keywordizer (fn [h]
(fn [r]
(h (update-in r [:params] #(clojure.walk/keywordize-keys %)))))]
(-> get-uri keywordizer rparams/wrap-params)))
Here is a sample run:
(get-uri-wrapped {:uri "/uri" :query-string "para1=a¶2=3" :request-method :get})
{:status 200,
:headers {"Content-Type" "text/html; charset=utf-8"},
:body "{:k1 \"a\", :k2 4}"}
This function takes a list of a files and is supposed to return a list of artists:
(defn get-artists [files]
(map #(.get-artist (->Mp3 %)) files))
Here the rest of the code:
(ns musicdb.filesystem
(:use [green-tags.core]))
(import '(java.io.File) '(java.net.url) '(java.io))
(require '[clojure.string :as str])
(defn get-files [search-path]
(let [directory (clojure.java.io/file search-path)
files (file-seq directory)
fonly (filter #(.isFile %) files)]
(map #(last (str/split (.toString %) #"/")) fonly)))
(defprotocol MusicFile
(get-artist [this])
(get-song [this])
(get-album [this]))
(defrecord Mp3 [filename]
MusicFile
(get-artist [this]
(:artist (get-all-info filename)))
(get-song [this]
(:title (get-all-info filename)))
(get-album [this]
(:album (get-all-info filename))))
And here are my tests:
(ns musicdb.core-test
(:require [clojure.test :refer :all]
[musicdb.core :refer :all]
[musicdb.filesystem :refer :all]
[clojure.pprint :refer :all]
))
(deftest test_0
(testing "getFiles returns valid result"
(is (> (count (get-files "/home/ls/books/books")) 50))))
(deftest test_1
(testing "check for file included"
(is (some #{"02 Backlit.mp3"} (get-files "/home/ls/Musik")))))
(deftest test_2
(testing "creating music file record"
(let [myfile (->Mp3 "/home/ls/Musik/Panopticon/02 Backlit.mp3")]
(is (= "Isis" (.get-artist myfile)))
(is (= "Backlit" (.get-song myfile))))))
(deftest test_3
(testing "testing get-artists"
(let [artists (get-artists (get-files "/home/ls/Musik"))
]
(is (> (count artists) 10)))))
(deftest test_4
(testing "testing get-artists check for artist"
(let [artists (get-artists (get-files "/home/ls/Musik"))
]
(is (some #{"Isis"} artists))))) ;artists is [nil nil nil ...]
From this tests only the last fails, which returns a list of nils.
If you want to reproduce ths be sure to include the green-tags dependency in your leiningen project.clj:
[green-tags "0.3.0-alpha"]
Your get-files function doesn't return the full path of the file so get-all-info just returns nil (https://github.com/DanPallas/green-tags/blob/master/src/green_tags/core.clj#L59 in combination with https://github.com/DanPallas/green-tags/blob/master/src/green_tags/core.clj#L120).
Here is a simple example that works:
(map (comp :artist get-all-info)
(filter #(.isFile %)
(file-seq (java.io.File. "/home/vema/Downloads/mp3"))))
;=> ("Yo Yo Honey Singh (DJJOhAL.Com)")
(Humoristic?) disclaimer: The MP3 should not be taken as an example of my musical taste, it was just the first free MP3 I found online.
I'm trying to build a small om-component that uses goog.fx.Dragger to make a target draggable. I require goog.fx.Dragger :as dragger and like so:
(ns om-draggable-me.core
(:require [om.core :as om :include-macros true]
[om.dom :as dom :include-macros true]
[goog.fx.Dragger :as dragger]))
When i use it like:
(defn draggable [app owner]
(reify
om/IDidMount
(did-mount [_]
(let [target (om/get-node owner)]
(dragger. target)))
om/IRender
(render [_]
(dom/div #js {:id "drag-box" :className "drag-box"}))))
I get the compiler warning:
WARNING: Use of undeclared Var om-draggable-me.core/dragger at line 22 src/cljs/om_draggable_me/core.cljs
Seen other examples of the use of goog.fx.DragDrop so why does not this work?
In your require form, dragger is a namespace alias
[goog.fx.Dragger :as dragger]
which is intended to be used like this:
dragger/your-var
;; the same as goog.fx.Dragger/your-var
while in your script, it was used as if it's a var:
(dragger. target)
and, as the compiler complained, such var hasn't been defined.
You can see people refer to clojure.string like this:
(require '[clojure.string :as str])
without worrying about conflicting with clojure.core/str function:
(str/join ", " [1 2 3])
(str "hello" "world")
For some reason the alias did not work, it works with fully qualified name:
(did-mount [_]
(let [target (om/get-node owner)]
(goog.fx.Dragger. target)))
I'm trying to add the following Hiccup template function to my file
(defn d3-page [title js body & {:keys [extra-js] :or {extra-js []}}]
(html5
[:head
[:title title]
(include-css "/css/nv.d3.css"))
(include-css "/css/style.css")]
[:body
(concat
[body]
[(include-js "http://d3js.org/d3.v3.min.js")
(include-js (str "https://raw.github.com"
"/novus/nvd3"
"/master/nv.d3.min.js")]
(map include-js extra-js)
[(include-js "/js/script.js")
(javascript-tag js)])]))
but keep getting an unmatched delimiter when I run lein ring server. This comes from Clojure Data Cookbook, so I am surprised to find an error and suspect the error is just on my end. Below is the rest of the code in the file:
(ns web-viz.web
(:require [compojure.route :as route]
[compojure.handler :as handler]
[clojure.string :as str])
(:use compojure.core
ring.adapter.jetty
[ring.middleware.content-type :only
(wrap-content-type)]
[ring.middleware.file :only (wrap-file)]
[ring.middleware.file-info :only
(wrap-file-info)]
[ring.middleware.stacktrace :only
(wrap-stacktrace)]
[ring.util.response :only (redirect)]
[hiccup core element page]
[hiccup.middleware :only (wrap-base-url)]))
(defn d3-page...as above
...)
(deftype Group [key values])
(deftype Point [x y size])
(defn add-label [chart axis label]
(if-not (nil? label)
(.axisLabel (aget chart axis) label)))
(defn add-axes-labels [chart x-label y-label]
(doto chart (add-label "xAxis" x-label)
(add-label "yAxis" y-label)))
(defn populate-node [selector chart groups transition continuation]
(-> (.select js/d3 selector)
(.datum groups)
(.transition)
(.duration (if transition 500 0))
(.call chart)
(.call continuation)))
(defn force-layout-plot []
(d3-page "Force-Directed Layout"
"webviz.force.force_layout();"
[:div#force.chart [:svg]]))
(defroutes site-routes
(GET "/force" [] (force-layout-plot))
(GET "/force/data.json" []
(redirect "/data/census-race.json"))
(route/resources "/")
(route/not-found "Page not found"))
(def app (-> (handler/site site-routes)))
In line 5
(include-css "/css/nv.d3.css"))
There's an extra ) there.
And in line 13,
"/master/nv.d3.min.js")]
There's one ) missing. Should be
"/master/nv.d3.min.js"))]
You should use an editor which can do the matching of braces, parentheses, and brackets, etc. automatically.