Get Clojure map from POST request - clojure

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.

Related

Convert Clojure schema input to lower case.

I can validate clojure schema inputs as string as shown below.
name :- s/Str,
place :- s/Str,
How can I write a defschema for Email to use like
email : - s/Email
which will convert/coerce it to lower case.
I happen to have exactly the one...
(ns myapp.utils.schema
(:import [org.apache.commons.validator.routines EmailValidator])
(:require [clojure.string :as str]
[schema.core :as s]
[schema.coerce :as sco]))
(def valid-email? "Checks that the value is a valid email string."
(let [ev (EmailValidator/getInstance)]
(fn [s]
(and
(string? s)
(.isValid ev, ^String s)
))))
(def Email "Email schema type, stricter than schema.core/Str."
(s/pred valid-email?))
(defn sanitize-email [s]
(if (string? s)
(-> s str/lower-case str/trim)
s))
(defn email-matcher [s]
(when (= Email s) sanitize-email))
(def matcher
"The Plumatic matcher used for Plumatic schema coercion in myapp."
(sco/first-matcher [email-matcher,,,]))
(defn coercer
[schema]
(sco/coercer schema matcher))
;; ...
(ns myapp.customer
(:require [schema.core :as s]
[myapp.utils.schema :as sc]))
(def Customer
{:customer/email sc/Email
:customer/name s/Str})
(def coerce-customer (sc/coercer Customer))
(coerce-customer {:customer/email "vAlENTin#gmaIl.com "
:customer/name "Valentin"})
=> {:customer/email "valentin#gmail.com"
:customer/name "Valentin"}

How to convert HTML tag with style to Hiccup? React problems

I'm trying to parse HTML with CSS into Hiccup in a Reagent project. I am using Hickory. When I parse HTML with inline CSS, React throws an exception.
(map
as-hiccup (parse-fragment "<div style='color:red'>test</div>")
)
The above generates [:div {:style color:red} "test"] & Reactjs returns exception from Reactjs:
Violation: The style prop expects a mapping from style properties to values, not a string.
I believe [:div {:style {"color" "red"}} "test"] must be returned instead.
Here is the code view:
(ns main.views.job
(:require [reagent.core :as reagent :refer [atom]]
[hickory.core :refer [as-hiccup parse parse-fragment]]))
(enable-console-print!)
(defn some-view [uid]
[:div
(map as-hiccup (parse-fragment "<div style='color:red'>test</div>"))
])
The whole repo is here and it works. I added the parsing from style tag to a map for React in the core.cljs file:
(ns hickory-stack.core
(:require [clojure.string :as s]
[clojure.walk :as w]
[reagent.core :as reagent :refer [atom]]
[hickory.core :as h]))
(enable-console-print!)
(defn string->tokens
"Takes a string with syles and parses it into properties and value tokens"
[style]
{:pre [(string? style)]
:post [(even? (count %))]}
(->> (s/split style #";")
(mapcat #(s/split % #":"))
(map s/trim)))
(defn tokens->map
"Takes a seq of tokens with the properties (even) and their values (odd)
and returns a map of {properties values}"
[tokens]
{:pre [(even? (count tokens))]
:post [(map? %)]}
(zipmap (keep-indexed #(if (even? %1) %2) tokens)
(keep-indexed #(if (odd? %1) %2) tokens)))
(defn style->map
"Takes an inline style attribute stirng and converts it to a React Style map"
[style]
(tokens->map (string->tokens style)))
(defn hiccup->sablono
"Transforms a style inline attribute into a style map for React"
[coll]
(w/postwalk
(fn [x]
(if (map? x)
(update-in x [:style] style->map)
x))
coll))
;; Test Data
(def good-style "color:red;background:black; font-style: normal ;font-size : 20px")
(def html-fragment
(str "<div style='" good-style "'><div id='a' class='btn' style='font-size:30px;color:white'>test1</div>test2</div>"))
;; Rendering
(defn some-view []
[:div (hiccup->sablono
(first (map h/as-hiccup (h/parse-fragment html-fragment))))])
(reagent/render-component [some-view]
(. js/document (getElementById "app")))

Is there some way to let compojure support type conversion automatically?

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&para2=3" :request-method :get})
{:status 200,
:headers {"Content-Type" "text/html; charset=utf-8"},
:body "{:k1 \"a\", :k2 4}"}

Can't require goog.fx.Dragger in ClojureScript

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)))

Hiccup template function

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.