Clojure defrecord serialization ClassNotFoundException - serialization

I was trying to serialise one of my records to a human readable format. While serialising using Java serialiser worked fine I am trying to use print-dup. The problem I am facing is that while writing the record goes fine reading the record results in clojure.lang.LispReader$ReaderException: java.lang.ClassNotFoundException: common.dummy.Doodh. Am I messing up the namespaces or something?
Please note that this is not a problem with Java serialisation.
Code below in the simplest form
(ns common.dummy)
(defrecord Doodh [id name])
(defn output [filename obj]
(def trr(map->Doodh {:id "moooh" :name "Cows"}))
(def my-string (binding [*print-dup* true] (pr-str trr)))
(spit filename my-string)
)
(defn pull [filename]
(def my-data (with-in-str (slurp filename) (read)))
(println my-data)
)
text file contents:
#common.dummy.Doodh["moooh", "Cows"]

Don't use def inside function definitions. When you are using def, you create a var in your namespace and possibly manipulate it as a side-effect with every function call. Use let-blocks.
If you want to save Clojure data structures in a file, use clojure.edn. It is safe (e. g. without your knowledge, no functions defined in the file will be invoked) but it allows to enable custom readers (see more below).
A type defined with defrecord can be printed in a (Clojure-reader-)readable way using pr-str (thanks to #A. Webb for noting that). In your example, I don't see why you would not stick to a hash-map in the first place, but if you really need a defrecord here, you may convert it into a readable string before your write it to the file.
(defrecord Doodh [id name])
(defn output [filename obj]
(spit filename (pr-str obj))
(defn pull [filename]
(with-in-str (slurp filename)
(read)))
This way of doing it has several disadvantages.
Using read makes your code vulnerable to function calls in the slurped files (like #=(java.lang.System/exit 0)).
An exception will be thrown when the file at filename is empty.
Finally your saved file will become incompatible to your code when you move your defrecord declaration to another namespace.
All three disadvantages can be avoided by using the edn-reader.
Using a custom reader with EDN
We extend our type Doodh by implementing the toString method of the java.lang.Object interface:
(defrecord Doodh [id name]
Object
(toString [this] (str "#Doodh" (into {} this))))
Because spit uses str, we can now omit the output function and simply invoke spit from e. g. the REPL:
(spit "Doodh.edn" (map->Doodh {:id "134" :name "Berta"}))
Doodh.edn:
#Doodh{:id 134, :name "Berta"}
Now to make sure that the Doodh will be read back, we invoke clojure.edn/read-string with a custom reader function:
(defn pull [filename]
(->> (slurp filename)
(clojure.edn/read-string {:readers {'Doodh map->Doodh}})))
If you read back "Doodh.edn" using the new pull, you should receive a valid Doodh. At the REPL:
(pull "Doodh.edn")
=> #user.Doodh{:id 134, :name "Berta"}

Related

"Update can not be used in this context"

I am new to clojure and I am working on a problem...
I'm am trying to find the frequency of all the instructors I have in a file, but I have no idea where to start. I did this before in a different program and it worked, but now i am getting an error that reads "Update can not be used in this context"
(defn read-lines [filename]
(with-open [rdr (clojure.java.io/reader filename)]
(doall (line-seq rdr))))
(defn classes [s]
(reduce conj (map hash-map [:semester :title :crn :code :levels :credits
:campus :section :capacity :actual :starthout :startmin
:endhour :endmin :weekday :room :datestart
:dateend :schtypye :instructor :prereq :corereq] (.split s ";"))))
(println(map classes (read-lines "C:/Users/Rohil's Computer/Desktop/textfile.txt")))
(loop [semester->instructor {}
[{:keys [semester instructor] :as row} & rows] classes]
(if (nil? row)
semester->instructor
(recur (update semester->instructor semester (fnil conj []) instructor) rows)))
Since you really didn't ask a specific question I'm going to extrapolate given what you said and the code that you posted.
The real question is "Where do I start?"
In Clojure that answer is always "at the repl"
so first you defined read-lines, which seems to take a file name and returns a collection with each item in the collection being the line of the file passed in
That's simple enough and easy to test at the repl.
but then you have the classes function which seems to take a semi-colon delimited string and tries to split it at the semi colon to make a map of the hard-coded keys to values in the string.
After that it kind of goes off the rails.
So lets pick up before that and look at a simpler example; follow along at the repl
(defn classes [s]
(reduce conj (map hash-map [:instructor :semester]
(.split s ";"))))
(map classes ["bob;1" "jack;2" "bob;3"]) returns a collection of maps. in this case it returns ({:semester "1", :instructor "bob"}
{:semester "2", :instructor "jack"}
{:semester "3", :instructor "bob"})
we want to get the frequencies of all names so lets pull those out into a simple list by mapping the instructor keyword over this collection of maps like so:
(map :instructor (map classes ["bob;1" "jack;2" "bob;3"])) so we get
("bob" "jack" "bob")
now we can just call frequencies on that collection to get a map of name to frequency like this:
(frequencies (map :instructor (map classes ["bob;1" "jack;2" "bob;3"])))
this returns {"jack" 1, "bob" 2}
Now that each of those individual pieces seems to be working at the repl we can put them all together:
(->> (read-lines "C:/Users/Rohil's Computer/Desktop/textfile.txt")
(map classes)
(map :instructor)
frequencies)

Count the frequency of a string in map in clojure

I am fairly new to clojure and just practicing some exam questions to prepare for a final exam.
I am trying to find the frequency of all the names is in a file. I read the file line by line and I save each string if it contains a specific keyword in a map. Since I do not want any repetitions, I am trying put distinct out in front of it, but I still keep getting the repeating elements.
(defn readFile []
(map (fn [line] (clojure.string/split line #";"))
(with-open [rdr (reader "C:/Users/Rohil's Computer/Desktop/textfile.txt")]
(doseq [[idx line] (map-indexed vector(line-seq rdr))]
(if(.contains line "2007")
(if(.contains line "May")
(if(not(.contains line "Batman"))
(map save [(nth(clojure.string/split line #";")3 (nth(clojure.string/split line #";")19)])
(distinct(map))
)
)
)
)
)
)
)
Here is my sample output:
I want to get rid of the 2 iron man elements.
(May 2007 Spiderman)
Clojure2.clj:
(March 2007 Iron man)
Clojure2.clj:
(March 2007 Iron man)
Clojure2.clj:
(April 2007 Captain America)
Is there anything I am missing?
With Clojure a good approach is to break down what you need to accomplish a task into functions that are as simple as possible. Once you have written and tested those simple functions you will be able to combine them together. Often times, the simple function you need already exists, as is here the case with frequencies:
(frequencies ["foo" "bar" "foo" "quux" "foo"])
=> {"foo" 3, "bar" 1, "quux" 1}
So it sounds like all you need to do really is tokenize the input file and apply frequencies to the list of tokens.

Functional way to make multiple API requests with Clojure

I'm working on a Clojure application that will interact with a web API to return a random result meeting a specific criterion. Because of the limitations of the API, I have to retrieve a number of results and use Clojure to filter out an appropriate one.
I would say 99% or more of the time, at least one of the results from the first request will meet my criterion. But because of that other 1%, I feel I have to build in functionality to make multiple requests. Obviously the odds are pretty good that the second request will be successful but I'm leery about writing a function that recurs endlessly until it gets the right result in case something goes wrong and I end up DDoSing the API.
My first thought was to build a counter into the function and limit it to, say, five requests. But that feels a bit procedural as an approach. What's the idiomatic Clojure way to go about this?
Here's what I have so far:
(ns randchar.api
(:require [clj-http.client :as client]))
(defn api-request
[url]
(get-in
(client/request
{:url url
:method :get
:content-type :json
:as :json}) [:body :results]))
(defn filter-results
"Returns the first result that meets the criterion."
[results criterion]
(take 1
(filter #(= (get-in % [:nested :sub-nested]) criterion) results)))
(defn build-url
"Returns a url with randomized query string for the api request; not shown."
[]
)
(defn return-result
"Currently recurs endlessly if no results meet the criterion."
[criterion]
(let [x (filter-results (api-request (build-url)) criterion)]
(if (not (empty? x))
x
(recur))))
You can try something like:
(defn return-result
[criterion count]
(->> (repeatedly count #(filter-results (api-request build-url) criterion))
(filter (complement empty?))
first))

Does clooj evaluates line by line or by the whole line

It seems when i evaluate the whole file it does not have an issue.
(ns ShipDataRecord
(:import [java.util.Date]
[org.joda.time.DateTime]
[org.joda.time.Seconds]
[org.joda.time.format.*]
[semsav.RecordSplitter]))
(require '[clojure.data.csv :as csv]
'[clojure.java.io :as io])
(defrecord Record [W1 W2 W3])
(defn read-csv [fname count]
(with-open [file (reader fname)]
(doall (take count (map (comp first csv/read-csv)
(line-seq file))))))
(map #(apply ->Record %) (read-csv "test.csv" 1))
However, when i evaluate line by line it seems to give me a problem.
Hence after evaluating the file, i ran this line in the REPL
(:W3 (first Record))
but it gives me a compiler exception of IllegalArgumentException Don't know how to create ISeq from: java.lang.Class clojure.lang.RT.seqFrom (RT.java:494).
I have googled around but i cant seem to find the problem. I have asked this in another question but as the words are too long i have to create a new question
In (:W3 (first Record)) the symbol Record represents class Record. I guess from your previous post you just want to get :W3 field from first record of collection of records produced by (map #...). So all you need is to get this value right from map expression or from variable where you can store result of expression:
(:W3 (first (map #(apply ->Record %) (read-csv "1.csv" 1))))
or
(def records (map #(apply ->Record %) (read-csv "1.csv" 1)))
(:W3 (first records))

Passing options to JS function with Amber

I'm trying to write the equivalent of:
$( "#draggable" ).draggable({ axis: "y" });
in Amber smalltalk.
My guess was: '#draggable' asJQuery draggable: {'axis' -> 'y'} but that's not it.
Not working on vanilla 0.9.1, but working on master at least last two months ago is:
'#draggable' asJQuery draggable: #{'axis' -> 'y'}
and afaict this is the recommended way.
P.S.: #{ 'key' -> val. 'key2' -> val } is the syntax for inline creation of HashedCollection, which is implemented (from the aforementioned two-month ago fix) so that only public (aka enumerable) properties are the HashedCollection keys. Before the fix also all the methods were enumerable, which prevented to use it naturally in place of JavaScript objects.
herby's excellent answer points out the recommended way to do it. Appearently, there is now Dictionary-literal support (see his comment below). Didn't know that :-)
Old / Alternate way of doing it
For historical reasons, or for users not using the latest master version, this is an alternative way to do it:
options := <{}>.
options at: #axis put: 'y'.
'#draggable' asJQuery draggable: options.
The first line constructs an empty JavaScript object (it's really an JSObjectProxy).
The second line puts the string "y" in the slot "axis". It has the same effect as:
options.axis = "y"; // JavaScript
Lastly, it is invoked, and passed as a parameter.
Array-literals vs Dictionaries
What you were doing didn't work because in modern Smalltalk (Pharo/Squeak/Amber) the curly-brackets are used for array-literals, not as an object-literal as they are used in JavaScript.
If you evaluate (print-it) this in a Workspace:
{ #elelemt1. #element2. #element3 }.
You get:
a Array (#elelemt1 #element2 #element3)
As a result, if you have something that looks like a JavaScript object-literal in reality it is an Array of Association(s). To illustrate I give you this snippet, with the results of print-it on the right:
arrayLookingLikeObject := { #key1 -> #value1. #key2 -> #value2. #key3 -> #value3}.
arrayLookingLikeObject class. "==> Array"
arrayLookingLikeObject first class. "==> Association"
arrayLookingLikeObject "==> a Array (a Association a Association a Association)"
I wrote about it here:
http://smalltalkreloaded.blogspot.co.at/2012/04/javascript-objects-back-and-forth.html