Casting DynamicLabel to Label in Clojure for Neo4j Embedded - clojure

I am trying to rewrite the neo4j sample code located here in clojure. But when I try to create a node, I get the following error
ClassCastException Cannot cast org.neo4j.graphdb.DynamicLabel to [Lorg.neo4j.graphdb.Label; java.lang.Class.cast (Class.java:3094)
Here is my code:
(ns neotest.handler
(:import (org.neo4j.graphdb
DynamicLabel
GraphDatabaseService
Label
Node
ResourceIterator
Transaction
factory.GraphDatabaseFactory
schema.IndexDefinition
schema.Schema)))
(def db
(let [path "C:\\Users\\xxx\\code\\neotest\\resources\\db1"]
(. (new GraphDatabaseFactory) (newEmbeddedDatabase path))))
(defn create-node []
(try (let [tx (. db beginTx)
l (. DynamicLabel (label "User"))]
(. db (createNode l))
(. tx success))))
I have tried type-hinting of all kinds and in all places, and I still get the same error.

It's because of the varargs Label... parameter. This was a bit of Clojure/Java interop I didn't know about: you have to pass the parameter in as an array (even if there's only one), so you need to do something like:
(. db (createNode (into-array Label [l])))
to make it work. There's another afternoon I won't be getting back!

the calls to dynamicLabel in the example java code look like:
DynamicLabel.label( "User" )
which would translate to:
(DynamicLabel/label "user")
because label is a static method of the class org.neo4j.graphdb.DynamicLabel which has the signature:
static Label label(String labelName)

Related

How can I get the var of a multimethod?

I'm trying to use dire to add hooks to multimethods. The author says it might not work.
Here is an example with a normal function:
(ns mydire.prehook
(:require [dire.core :refer [with-pre-hook!]]))
(defn times [a b]
(* a b))
(with-pre-hook! #'times
"An optional docstring."
(fn [a b] (println "Logging something interesting.")))
(times 21 2) ; => "Logging something interesting."
As you can see, with-pre-hook! is passed (var times) (which is the same as #'times).
The problem is that when calling var for a multimethod I'm getting an exception:
clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol
Is there a way to make this work?
Below is my code sample:
(defmulti get-url identity)
(defmethod get-url :stackoverflow
[site]
"http://stackoverflow.com")
(with-pre-hook! (var (get-method get-url :stackoverflow))
(fn [x] (println "getting url for stackoverflow.")))
var is a macro, it does not evaluate its argument. If you give it a list, it will not evaluate the list, it will reject it, because it's a list and not a symbol.
There is no var to attach to with a specific method, because defmethod does not create a var, it modifies the dispatch of the multimethod it is attached to. The value returned by get-method is a function, not a var.
Having looked at dire, it specifically needs a var to act on, and won't work on a specific method of a multimethod without some amount of redesign. So no, you can't use with-pre-hook on a specific method, though it might work on a multimethod itself (including all of its methods).

ClassCastException when calling Clojure function

I have written the following bit of code
(defn create [title url]
(when (not-any? clojure.string/blank? '(title url))
(println "doing stuff")))
However when I call the function
(create "title" "url")
I get the following error and cannot figure out what I am doing wrong
ClassCastException clojure.lang.Symbol cannot be cast to java.lang.CharSequence clojure.string/blank? (string.clj:279)
This is one of the things that also tripped me up when I first started learning clojure. Basically, you should use
(list title url)
The clojure compiler treats
'(title url)
as
(quote (title url))
'quote' does not evaluate anything inside of it, so 'title' and 'url' are just symbols (clojure.lang.Symbols to be precise).
Here's a better explanation than mine: http://blog.8thlight.com/colin-jones/2012/05/22/quoting-without-confusion.html

Clojure string splitting results in IllegalArgumentException

CLOJURE
Hi everyone, I am new to clojure. I would like to update my record with a split string.
(defrecord Learning [Name Age Gender])
(def person [:Name :Age :Gender])
(let person
(clojure.string/split "John,12,Male" #","))
I am able to split the string but it throws an exception
IllegalArgumentException let requires a vector for its binding in ShipDataRecord:1 clojure.core/let (core.clj:3965)
Can Someone kindly explain how should I go about doing it?
Looks like you missed a lot.
First, you using def the wrong way. All variables in clojure are immutable. So, after you defined some variable you can't change its value, but you can rebind it with a new value in any local context using let.
Second, your using of let is incorrect. Try to read Clojure Docs:
(let [x 1]
x)
let creates new context by binding some variables with new values. [x 1] means that you bind value 1 to the variable x. But outside of let x won't change.
What you want to do is:
(defrecord Learning [Name Age Gender])
(def person
(apply ->Learning
(clojure.string/split "John,12,Male" #",")))

Unable to get random (doc) from a namespace

I want to display random (doc) page for some namespace.
The random function name I can get by:
user=> (rand-nth (keys (ns-publics 'clojure.core)))
unchecked-char
When I try to pass this to (doc) I get this:
user=> (doc (rand-nth (keys (ns-publics 'clojure.core))))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol clojure.core/ns-resolve (core.clj:3883)
I'm new to Clojure and I'm not sure how to deal with this... I tried to convert this into regexp and use (find-doc) but maybe there is a better way to do this...
Explanation
The problem here is that doc is a macro, not a function. You can verify this with the source macro in the repl.
(source doc)
; (defmacro doc
; "Prints documentation for a var or special form given its name"
; {:added "1.0"}
; [name]
; (if-let [special-name ('{& fn catch try finally try} name)]
; (#'print-doc (#'special-doc special-name))
; (cond
; (special-doc-map name) `(#'print-doc (#'special-doc '~name))
; (resolve name) `(#'print-doc (meta (var ~name)))
; (find-ns name) `(#'print-doc (namespace-doc (find-ns '~name))))))
If you're new to Clojure (and lisps), you might not have encountered macros yet. As a devastatingly brief explanation, where functions operate on evaluated code, macros operate on unevaluated code - that is, source code itself.
This means that when you type
(doc (rand-nth (keys (ns-publics 'clojure.core))))
doc attempts to operate on the actual line of code - (rand-nth (keys (ns-publics 'clojure.core))) - rather than the evaluated result (the symbol this returns). Code being nothing more than a list in Clojure, this is why the error is telling you that a list can't be cast to a symbol.
Solution
So, what you really want to do is evaluate the code, then call doc on the result. We can do this by writing another macro which first evaluates the code you give it, then passes that to doc.
(defmacro eval-doc
[form]
(let [resulting-symbol (eval form)]
`(doc ~resulting-symbol)))
You can pass eval-doc arbitrary forms and it will evaluate them before passing them to doc. Now we're good to go.
(eval-doc (rand-nth (keys (ns-publics 'clojure.core))))
Edit:
While the above works well enough in the repl, if you're using ahead ahead-of-time compilation, you'll find that it produces the same result every time. This is because the resulting-symbol in the let statement is produced during the compilation phase. Compiling once ahead of time means that this value is baked into the .jar. What we really want to do is push the evaluation of doc to runtime. So, let's rewrite eval-doc as a function.
(defn eval-doc
[sym]
(eval `(doc ~sym)))
Simple as that.

Trouble with building up a string in Clojure

[this may seem like my problem is with Compojure, but it isn't - it's with Clojure]
I've been pulling my hair out on this seemingly simple issue - but am getting nowhere.
I am playing with Compojure (a light web framework for Clojure) and I would just like to generate a web page showing showing my list of todos that are in a PostgreSQL database.
The code snippets are below (left out the database connection, query, etc - but that part isn't needed because specific issue is that the resulting HTML shows nothing between the <body> and </body> tags).
As a test, I tried hard-coding the string in the call to main-layout, like this:
(html (main-layout "Aki's Todos" "Haircut<br>Study Clojure<br>Answer a question on Stackoverfolw")) - and it works fine.
So the real issue is that I do not believe I know how to build up a string in Clojure. Not the idiomatic way, and not by calling out to Java's StringBuilder either - as I have attempted to do in the code below.
A virtual beer, and a big upvote to whoever can solve it! Many thanks!
=============================================================
;The master template (a very simple POC for now, but can expand on it later)
(defn main-layout
"This is one of the html layouts for the pages assets - just like a master page"
[title body]
(html
[:html
[:head
[:title title]
(include-js "todos.js")
(include-css "todos.css")]
[:body body]]))
(defn show-all-todos
"This function will generate the todos HTML table and call the layout function"
[]
(let [rs (select-all-todos)
sbHTML (new StringBuilder)]
(for [rec rs]
(.append sbHTML (str rec "<br><br>")))
(html (main-layout "Aki's Todos" (.toString sbHTML)))))
=============================================================
Again, the result is a web page but with nothing between the body tags. If I replace the code in the for loop with println statements, and direct the code to the repl - forgetting about the web page stuff (ie. the call to main-layout), the resultset gets printed - BUT - the issue is with building up the string.
Thanks again.
~Aki
for is lazy, and in your function it's never being evaluated. Change for to doseq.
user> (let [rs ["foo" "bar"]
sbHTML (new StringBuilder)]
(for [rec rs]
(.append sbHTML (str rec "<br><br>")))
(.toString sbHTML))
""
user> (let [rs ["foo" "bar"]
sbHTML (new StringBuilder)]
(doseq [rec rs]
(.append sbHTML (str rec "<br><br>")))
(.toString sbHTML))
"foo<br><br>bar<br><br>"
You could also use reduce and interpose, or clojure.string/join from clojure.string, or probably some other options.
user> (let [rs ["foo" "bar"]]
(reduce str (interpose "<br><br>" rs)))
"foo<br><br>bar"
user> (require 'clojure.string)
nil
user> (let [rs ["foo" "bar"]]
(clojure.string/join "<br><br>" rs))
"foo<br><br>bar"
You would like to use the re-gsub like this:
(require 'clojure.contrib.str-utils) ;;put in head for enabling us to use re-gsub later on
(clojure.contrib.str-utils/re-gsub #"\newline" "<br><br>" your-string-with-todos-separated-with-newlines)
This last line will result in the string you like. The require-part is, as you maybe already know, there to enable the compiler to reach the powerful clojure.contrib.str-utils library without importing it to your current namespace (which could potentially lead to unnescessary collisions when the program grows).
re- is for reg-exp, and lets you define a reg-exp of the form #"regexp", which to replace all instances that is hit by the regexp with the argument afterwards, applied to the third argument. The \newline is in this case clojures way of expressing newlines in regexps as well as strings and the character we are looking for.
What I think you really wanted to do is to make a nifty ordered or unordered list in html-format. These can be done with [hiccup-page-helpers][2] (if you don't have them you probably have a compojure from the time before it got splited up in compojure, hiccup and more, since you use the html-function).
If you want to use hiccup-page-helpers, use the command re-split from the clojure.contrib.str-utils mentioned above in this fashion:
(use 'hiccup.page-helpers) ;;watch out for namespace collisions, since all the functions in hiccup.page-helpers got into your current namespace.
(unordered-list (clojure.contrib.str-utils/re-split #"\newline" your-string-with-todos-separated-with-newlines))
which should render a neat
<ul>
<li>todo-item1</li>
<li>todo-item2</li>
</ul>
(and yes, there is an ordered-list command that works the same way!)
In the last line of clojure code above, all you todos gets into a (list "todo1" "todo2") which is immediately consumed by hiccup.page-helpers unordered-list function and is there converted to an html-ized list.
Good luck with compojure and friends!