Clojure , how to pass correct param into function - clojure

I'm newbee in Clojure world. My pet project is writing of kafka consumer / producer. There a lot of topic in www ,but I've faced with misunderstanding.
There is a code
(ns producer
(:require [clojure.tools.logging :as log])
(:import (java.util Properties)
(org.apache.kafka.common.serialization StringSerializer)
(org.apache.kafka.clients.producer KafkaProducer ProducerRecord))
(:gen-class))
(defn create-kafka-producer [server]
(let [producer-props {
"value.serializer" StringSerializer
"key.serializer" StringSerializer
"bootstrap.servers" server }]
(KafkaProducer. producer-props)))
(defn send-single-message [producer topic-name record]
(.send producer (ProducerRecord. topic-name (str "Value: " (.value record)))))
(defn -main []
(def svr "localhost:8084")
(def producer (create-kafka-producer svr))
(send-single-message producer "Test msg"))
I want just pass some msg into kafka through send-single-message function. But as you see in code example is using (.value record) and when I'm trying to pass the "Test msg" string it crashed and show the followed error
Exception in thread "main" java.lang.IllegalArgumentException: No matching field found: value for class java.lang.String
I understand that is because I've pass string object, that has no .value
So, how is it possible to solve that issue? Thanks in advance
p.s. I've tried to pass other structure, but no results or different errors

As you noted, the error is because you're attempting to get the value field of the passed object, but you're passing in a String which doesn't have a value field.
Checking that ProducerRecord constructor signature, it takes a generic argument of type V (which is just an Object as far as Clojure is concerned). I'd just pass the String directly and omit the value field access.

Related

Clojure, re-graph fetched data from graphql successfully, but callback didn't activate

So I use re-graph version 0.1.11 and I try fetching data from the endpoint. After fetching data, I checked network tab in the browser and I found the expected data after that it should activate my callback function, but it doesn't seem to work (but sometimes it works and after refreshing the page a few times it doesn't work again). Here is the code.
;; how I init re-graph
(rf/dispatch [::re-graph/init
::conf/graphql-client-name
{:ws-url url
:http-url url
:ws-reconnect-timeout 500
:resume-subscriptions? true}])
(re-frame.core/reg-event-fx
::fetch-expected-data
(fn [cofx event]
(let [app-db (:db cofx)
some-params (-> event second (cljs.core/js->clj :keywordize-keys true))
token (-> app-db (lens/get-in (auth-db/lens-token :group-level-x)))]
(re-frame.core/dispatch
[:re-graph.core/query
::conf/graphql-client-name
"query findExpectedData($query: FetchExpectedDataInput!, $token: String!) {
findExpectedData(query: $query, token: $token){
value1
value2
...
}
}"
{:query some-params
:token token}
;; this is where the problem occurs
;; even though i found the data in the network tab, but
;; this callback doesn't seem to work (sometimes it works sometimes it doens't)
[::fetched-data-completed]]))))
(re-frame.core/reg-event-fx
::fetched-data-completed
(fn [cofx [_ {:keys [data errors] :as payload}]]
(let [app-db (:db cofx)
error-message (-> errors :errors first :message)]
(if (or (nil? errors) (empty? errors))
(do (bla bla when success))
(pr error-message)))))
I'm stuck with this problem for a few months. maybe because I fetch a lot of data at the same time? or could be something else anyone knows?. By the way the actual code I use defmacro, but it works the same way as the above code.
So I managed to find the answer to my own question. It seems like app-db has not been initialized properly so I fixed that problem and everything works fine. hope it helps someone who struggle with this problem.

How can I call a function in clojure and get a result?

I am trying to implement an amazon aws binding with Clojure, so that I can search for a book by sending an isbn and get an xml report of it. I am then trying to retrieve the salesrank and book title from the report. Here is what I am trying to use: https://github.com/FreeAgent/clj-amazon
Instead of using REPL, I made a core2.clj inside the src folder and added this code into it:
(ns clj_amazon.core2
(:use clj-amazon.core)
(:use clj-amazon.product-advertising)
(:gen-class))
(defn -main [& args]
(def ACCESS-KEY "my access code")
(def SECRET-KEY "my secret key" )
(def ASSOCIATE-ID "my id")
(def gibson-opus-search (with-signer (ACCESS-KEY, SECRET-KEY) (item-search :search-index "Books", :keywords "Neuromancer", :associate-tag ASSOCIATE-ID, :condition "New")))
(gibson-opus-search)
)
If I remove
(gibson-opus-search)
and run "lein run" in the command line, I get no errors. I have the correct access key/secret key, and it seems that the code is working fine. But I also get no report printed. I'm new to Clojure, so I'm not understanding what I should do next. I tried to call the function with
(gibson-opus-search)
, but then i get this error: wrong number of args(0) passed to persistentarraymap. How can I fix this?
You are defining gibson-opus-search with def which is for for assigning values, not defining functions. You need to define it with defn then call it with the right number of arguments.

Get information out of go block

I have the following ClojureScript code to make a POST request:
(defn perform-post [resource]
"Performs a post and returns the body :)"
(go (let [response (<! (http/post resource))]
(:body response))))
When I make a call to a resource which returns a number
(js/console.log (perform-post post-create-recipe-url))
This prints:
bufObject { buf={...}, n=1, cljs$lang$protocol_mask$partition0$=2,
more...}
bufObject { head=1, tail=0, length=1, meer...}
arr
["6276677237104933520", undefined]
I want to obtain the "6276677237104933520" (the post body) information as a "return" value.
How can I accomplish this? I tried <!! but it does not work since it is not defined.
Blocking semantics (<!!) is not available on ClojureScript platform.
You can retrieve value from a channel only within go block:
(go (js/console.log (<! (perform-post post-create-recipe-url))))

Clojure - avoid duplicated code with monger requests

I am using Clojure and Monger
It works fine, and I group functions by the collection they relate to.
Therefore, every file begins like this :
(ns img-cli.model.mycollectionname
(:require [monger.core :as mg]
[monger.collection :as mc]
[edn-config.core :refer [env]])
(:import [com.mongodb MongoOptions ServerAddress DB WriteConcern]
[org.bson.types ObjectId]))
(def config (get-in env [:mongo]))
;; using MongoOptions allows fine-tuning connection parameters,
;; like automatic reconnection (highly recommended for production
;; environment)
(def ^MongoOptions opts (mg/mongo-options { :threads-allowed-to-block-for-connection-multiplier 300}))
(def ^ServerAddress sa (mg/server-address (:url config) (:port config)))
(def conn (mg/connect sa opts))
(def db (mg/get-db conn (:db config)))
(def collection-name "asset")
;; I have to write one like this every time
(defn find-one-as-map
"fetch asset by Id"
[^String id]
(mc/find-one-as-map db collection-name {:_id (ObjectId. id)}))
Code duplication has of course several disadvantages in itself.
Also I'm not sure if connections are properly pooled afterwards ?
How can I avoid doing this ?
I sense I could pass an additional "db" parameter to each function, but then where would it come from ?
If I create the db connection in the "entry" file of my program, then how could it be passed to every function from there ?
For instance let's says I have Compojure routes in different files :
;; in the main handler file
(def db ...) ;; if I move the previous db configuration
;; in here, it could be the only place where this is set
;; importing Compojure routes from different files
(defroutes routes-from-file1
routes-from-file2...)
Let's say that some functions called from some of the routes in "file2" need access to the db, how can I pass this variable to them ?
I also have a lot of repetitive code afterwards, for instance to get data by Id for every collection...
I feel this could be simplified, but I'm not sure how.
Just refer to it by its namespace
(ns foo
(:require [handler :as h]))
(println h/db)

Midje provided not stubbing function in Compojure / Ring handler

I'm attempting to use Midje to stub the view in a handler unit test, but my use of Midje's (provided) obviously isn't correct.
I've simplified and inlined the view to a (content) function in the handler:
(ns whattodo.handler
(:use compojure.core)
(:require [whattodo.views :as views]))
(defn content [] (views/index))
(defn index [] (content))
(defroutes app
(GET "/" [] (index)))
and am trying to test it using
(ns whattodo.t-handler
(:use midje.sweet)
(:use ring.mock.request)
(:use whattodo.handler))
(facts "It returns response"
(let [response (app (request :get "/"))]
(fact "renders index view" (:body response) => "fake-html"
(provided (#'whattodo.handler/content) => (fn [] "fake-html")))))
I was expecting the stubbed function to be called returning 'fake-html' and thus the unit test passing, but instead, the test fails as the real implementation is called - invoking the real view.
You don't need the function shortcut, just use (content) => .... As you have it right now, midje expects that your code calls literally (#content), but your index function calls (content) instead. Your confusion about midje's syntax might be that you expect that you assign to the function name the expected result, but that is not the case. You have to replace the exact call. I.e., if your index function would call content with some argument, you would have to account for this as well, e.g. by (provided (content "my content") => ...)
I discovered today that I had my scopes confused - the let block introducing response was outside of the fact call that included the provided. Thus the response was created before the provided was invoked.
Working code which passed this early test instead used the against-background call
(facts "It returns response"
(against-background (whattodo.handler/content) => "fake-html")
(let [response (app (request :get "/"))]
(fact "renders index view"
(:body response) => "fake-html")))