Errors when trying to pass args in clojure using clj-http - clojure

hopefully this is something simple for the more experienced out there. I am using clj-http and trying to pass the command line arg int it (to take a URL). I am an absolute Clojure beginer but I have managed to pass the args through to a ptintln which works.
(ns foo.core
(:require [clj-http.client :as client]))
(defn -main
[& args]
(def url (str args))
(println url)
(def resp (client/get url))
(def headers (:headers resp))
(def server (headers "server"))
(println server))
Error message
Ants-MacBook-Pro:target ant$ lein run "http://www.bbc.com"
("http://www.bbc.com")
Exception in thread "main" java.net.MalformedURLException: no protocol: ("http://www.bbc.com")
This works
(def resp (client/get "http://www.bbc.com"))
thanks in advance.

args is a list, which means that calling str on it returns the representation of the list, complete with parentheses and inner quotes, as you can see in your error trace:
(println (str '("http://www.bbc.com")))
;; prints ("http://www.bbc.com")
Of course, URLs don't start with parentheses and quotes, which is why the JVM tells you your URL is malformed.
What you really want to pass to get is not the string representation of your argument list, but your first argument:
(let [url (first args)]
(client/get url)) ;; Should work!
In addition, you should never use def calls within functions -- they create or rebind vars at the toplevel of your namespace, which don't want.
What you should be using instead is let forms, which create local variables (like url in my example). For more information on let, look at http://clojure.org/special_forms.
I'd probably structure your code like so:
(defn -main
[& args]
(let [url (first args)
resp (client/get url)
server (get-in resp [:headers "server"])]
(println url)
(println server)))

Related

line-seq freezes on java.io.BufferedReader in clojure

I'm trying to process an HTTP stream using clojure.
I am able to write the stream to a file, but I'm trying to process the messages using core.async.
I followed this answer here:
Processing a stream of messages from a http server in clojure
However when I call (line-seq ) on the java.io.BufferedReader, it freezes for me.
(defn trades-stream
[]
(let [session (new-session)
{:keys [url sessionid]} (:stream session)
dump-url (str url "?sessionid=" sessionid "&symbols=mu" )
lines (-> dump-url
(client/get {:as :stream})
:body
io/reader)]
(line-seq lines )))
Any idea how I would remidy this ? Thanks!
Note that line-seq is lazy and won't do anything until forced into a string or something. Perhaps try
(println (first (line-seq lines)))
or
(reduce conj [] (line-seq lines)) ; then print something
You can also use (slurp <input-stream>) to get the contents as a string.

How to use 'after' function to create interceptor in Pedestal

I wish to make a new Pedestal interceptor to be run during the leave stage. I wish to modify the context to add a token string to the base of each html page (for use in 'site alive' reporting).
From the Pedestal source code here I see this function:
(defn after
"Return an interceptor which calls `f` on context during the leave
stage."
([f] (interceptor {:leave f}))
([f & args]
(let [[n f args] (if (fn? f)
[nil f args]
[f (first args) (rest args)])]
(interceptor {:name (interceptor-name n)
:leave #(apply f % args)}))))
So I need to provide it with a function which will then be inserted into the interceptor map. That makes sense. However, how can I write this function making reference to the context when 'context' is not in scope?
I wish to do something like:
...[io.pedestal.interceptor.helpers :as h]...
(defn my-token-interceptor []
(h/after
(fn [ctx]
(assoc ctx :response {...}))))
But 'ctx' is not in scope? Thanks.
For what it's worth, we no longer think the before and after functions are the best way to do this. (All the functions in io.pedestal.interceptor.helpers are kind of unnecessary now.)
Our recommendation is to write interceptors just as Clojure map literals, like so:
(def my-token-interceptor
{:name ::my-token-interceptor
:leave (fn [context] (assoc context :response {,,,}))})
You can see that the after function doesn't add anything in terms of clarity or explanatory value.
Of course you can use a function value in the map rather than making an anonymous function right there:
(defn- token-function
[context]
(assoc context :response {,,,}))
(def my-token-interceptor
{:name ::my-token-interceptor
:leave token-function)})
the after doc is clear on this.
(defn after
"Return an interceptor which calls `f` on context during the leave
stage."
your f will receive context as its first argument. You can access context inside f by using f's first argument.
below is a sample of a f function: token-function, that will be supplied to h/after and because h/after returns interceptor, I create a 'my-token-interceptor' by calling h/after with token-function
...[io.pedestal.interceptor.helpers :as h]...
(defn token-function
""
[ctx]
(assoc ctx :response {}))
(def my-token-interceptor (h/after token-function))
;; inside above token-function, ctx is pedestal `context`

How to replace "->"?

I wonder, how can I replace this without "->"?
(defn -main [& args]
(->
"http://www.fsdfdsfds.com" URL. html-resource
print))
I tried this and it didn't print anything:
(defn -main [& args]
(print(URL. html-resource "http://www.fsdfdsfds.com"))
You can replace it like so:
(print (html-resource (URL. "http://www.fsdfdsfds.com")))
The way you have it, you are passing 2 args to URL. The thread-first macro passes your url string to URL., then html-resource, then to print.

My logging (with robert-hooke) does not work properly with tools.namespace/refresh, why?

EDIT: Turned out I was using require instead of :require in the namespace declaration. With :require, tools.namespace refreshes the logging namespace, and the problem goes away. I still find it curious, however, that the expression (eval `(var ~(symbol "A/func"))) does not work in the situation described below (that is, if B below is not refreshed).
Summary: I'm using tools.namespace. If I have namespaces A and B, and in B do (eval `(var ~(symbol "A/func"))), and (tools.namespace/refresh) and run the code, that works. But if I make a change to A, do (tools.namespace/refresh), so that only A refreshes, then running that expression gives the error: Cannot resolve var: A/func in this context, even though A/func exists. Why?
Longer version:
In my project, I have a logging module/namespace that uses robert-hooke (see below). I'm using tools.namespace to reload my code when I make changes.
The problem is the following: When I want to log (my logging currently just prints) something, I list the functions that I want to log in my logging namespace and do (t.n/refresh). That works. But if I make changes to the the namespaces that contain the functions that I want to log, and do (t.n/refresh) without making changes to the logging namespace, the logging no longer works (for the functions that have been refreshed). As soon as I make a change to logging, so that it too is refreshed by tools.namespace, it starts working again.
So, it's like the vars in namespaces that have been refreshed don't properly get their logging hooks. But I don't understand why.
Below is my logging namespace. I call add-logging-wrappers each time I run my program.
If I add (eval `(var ~(symbol "sv/register-damage"))) inside add-logging-wrappers, that's fine when logging has just been refreshed and the logging works. But those times the logging does not work, that expression causes the error Cannot resolve var: sv/register-damage in this context.
(ns game.logging
(require [robert.hooke :as rh]
[clojure.pprint :as pp]
[game.server.core :as sv]
[game.client.core :as cl]
[game.math :as math]
(game.common [core-functions :as ccfns]
[graphics :as gfx])
(game.server [pathfinding :as pf]))
(:use [game.utils]))
(defn log-println [name type object]
(println (str (current-thread-name) " // " name " " type ":\n"
(with-out-str
(pp/pprint object)))))
(defn print-output [name f & args]
(let [result (apply f args)]
(log-println name "output" result)
result))
(defn print-input [name f & args]
(log-println name "input" args)
(apply f args))
(defn print-call [name f & args]
(println (str (current-thread-name) "//" name))
(apply f args))
(defmacro make-name-var-list [fn-list]
`[~#(for [fn fn-list]
[(str fn) `(var ~fn)])])
(defmacro defloglist [name & fns]
`(def ~name (make-name-var-list [~#fns])))
(defn add-hooks [name-vars & wrappers]
(when (seq wrappers)
(doseq [[name var] name-vars]
(rh/add-hook var (partial (first wrappers) name)))
(recur name-vars (next wrappers))))
(defn get-ns-name-vars [ns-sym]
(-> (the-ns ns-sym) (#(.name %)) ns-interns))
(defn add-hooks-to-ns [ns-sym & wrappers]
(apply add-hooks (get-ns-name-vars ns-sym) wrappers))
(defloglist log-both
sv/distribute-exp ;; <--- things to log
sv/register-damage
sv/give-exp)
(defloglist log-input)
(defloglist log-output)
(defn add-logging-wrappers []
(dorun (->> (all-ns) (map #(.name %)) (mapcat ns-interns) (map second)
(map rh/clear-hooks)))
(add-hooks log-both print-output print-input)
(add-hooks log-input print-input)
(add-hooks log-output print-output))

How to get JSON post data in Noir

A while back, Chris Granger posted this middleware to get JSON hashes to appear in the defpage params under an umbrella "backbone" element.
(defn backbone [handler]
(fn [req]
(let [neue (if (= "application/json" (get-in req [:headers "content-type"]))
(update-in req [:params] assoc :backbone (json/parse-string (slurp (:body req)) true))
req)]
(handler neue))))
How could I modify this code to have the JSON elements appear as top-level params in defpage; i.e. get rid of the :backbone umbrella?
There are two things you can do. One option is to replace the value of :params with the map returned after parsing the JSON. In order to do that, just associate the new map to the :params key.
(assoc req [:params] (json/parse-string (slurp (:body req)) true))
The other option (as suggested by #dAni) is to merge the values of the parsed JSON into so that existing values in the :params map are not overridden. The reason why you need to use partial instead of just using merge here is because the final map is the merged result of the maps from left-to-right. Your solution works if you want the values from the JSON map to take precedence.
(update-in req [:params]
(partial merge (json/parse-string (slurp (:body req)) true)))
Got it. assoc just works for one element so you have to put everything under the :backbone umbrella. To push all the JSON elements into the params, you have to use merge. So change the 4th line to:
(update-in req [:params] merge (json/parse-string (slurp (:body req)) true))
If you don't mind pulling in another dependency, you can use the ring-middleware-format library.
Instructions:
Add [ring-middleware-format "0.1.1"] to your project.clj
and then in your server.clj, add the following code:
Code:
(:require [ring.middleware.format-params :as format-params])
(server/add-middleware format-params/wrap-json-params)
(defn -main [& m]
; Start the server...
)
Now any incoming JSON will be available to use just like form POSTdata.