Datomic not a valid :uri for attribute - clojure

I'm trying to insert a value on an attribute defined as
{:db/ident :foo/uri
:db/valueType :db.type/uri
:db/cardinality :db.cardinality/one
:db/doc "Test attribute of value type uri."}
I'm inserting
{:foo/uri "scheme://host/path"}
and get
Execution error (ExceptionInfo) at datomic.client.api.async/ares (async.clj:58).
Value scheme://host/path is not a valid :uri for attribute :foo/uri
I'm lost as to what to insert here. It should be a string, right? No reader literal?
I found zero examples of this online. Also took a look at the java class corresponding to this but no illumination. Or maybe it needs an instance of java.net.URI, so to put it in edn we'd need to install our own reader literal?

It does need to be an instance of java.net.URI.
If it's part of your code, you could wrap it up when creating it:
{:foo/uri (new java.net.URI "scheme://host/path")}
If you want to read it in from an edn file, then you could use a custom tag, say #URI, in the file and pass a custom reader function as an option to edn/read-string:
;;; in some file located at `path`
{:foo/uri #URI"scheme://host/path"}
;;; code to read the file at `path`
(require '[clojure.edn :as edn])
(-> path
slurp
(edn/read-string {:readers {'URI #(new java.net.URI %)}})
You could also do this with clojure.tools.reader, which allows for unsafe code execution (so probably not recommended):
;;; use reader tools
(require '[clojure.tools.reader :as r])
(r/read-string "{:foo/uri #=(new java.net.URI \"scheme://host/path\")}")

Related

compojure-api spec coercion on response body

I'm trying to figure out how to do custom coercion with compojure-api and spec. By reading the docs and code I have been able to do coercion on the input (the body) but am unable to do coercion on the response body.
Specifically, I have a custom type, a timestamp, that is represented as a long within my app but for the web API I want to consume and return ISO timestamps (no, I don't want to use Joda internally).
The following is what I have that works for input coercion but I have been unable to properly coerce the response.
(ns foo
(:require [clj-time.core :as t]
[clj-time.coerce :as t.c]
[spec-tools.conform :as conform]
[spec-tools.core :as st]))
(def timestamp (st/create-spec
{:spec pos-int?
:form `pos-int?
:json-schema/default "2017-10-12T05:04:57.585Z"
:type :timestamp}))
(defn json-timestamp->long [_ val]
(t.c/to-long val))
(def custom-json-conforming
(st/type-conforming
(merge
conform/json-type-conforming
{:timestamp json-timestamp->long}
conform/strip-extra-keys-type-conforming)))
(def custom-coercion
(-> compojure.api.coercion.spec/default-options
(assoc-in [:body :formats "application/json"] custom-json-
conforming)
compojure.api.coercion.spec/create-coercion))
;; how do I use this for the response coercion?
(defn timestamp->json-string [_ val]
(t.c/to-string val))
;; I've tried the following but it doesn't work:
#_(def custom-coercion
(-> compojure.api.coercion.spec/default-options
(assoc-in [:body :formats "application/json"] custom-json-
conforming)
(assoc-in [:response :formats "application/json"]
(st/type-conforming
{:timestamp timestamp->json-string}))
compojure.api.coercion.spec/create-coercion))
Problem is that Spec Conforming is a one-way transformation pipeline:
s/conform (and because of that st/conform) does both transform and validate for the result. Your response coercion first converts the integer into date string and the validates it against the original spec predicate, which is pos-int? and it fails on that.
To support two-way transformations, you need to define the end result as either of the possible formats: e.g. change your predicate to something like #(or (pos-int? %) (string? %)) and it should work.
Or you can have two different Spec Records, one for input (timestamp-long with pos-int? predicate) and another for outputs (timestamp-string with string? predicate). But one needs to remember to use correct ones for request & responses.
CLJ-2251 could possible help if there was and extra :transform mode (not written in the issue yet), which would do conforming without validating the end results.
Normally, return transformations are done by the format encoder, but they usually dispatch on value types. For example Cheshire just sees an Long and has no clue that it should be written as date string.
Maybe people on the #clojure-spec slack could help. Would also like to know how to build this kind of two-way transformation with spec.

Map literal must contain an even number of forms

I'm trying to make an requiest to a web service which requires data in json and a secret (:key)
(ns fdsfdsfds.core
(:require [clj-http.client :as client])
(:require [clojure.data.json :as json]))
(defn -main [& args]
(client/post "https://fsdfdsfd.com/api/fdsfds"
{:body {(json/write-str {:key "fdsfdsfdsfd"})}}))
I'm having an error:
Exception in thread "main" java.lang.RuntimeException: Map literal must contain an even number of forms
There're the even number of them, though.
The problem is here:
{:body { (json/write-str {:key "fdsfdsfdsfd"}) }}
^-- single item missing value? --^
^----- this is a map too
There's nothing paired with the function call.
The :body has a map as a value, but you only have a function in there, not a possible key for its value, or if that is the key, there's no value for it.
You probably want to remove the outer map brackets and leave:
{:body (json/write-str {:key "fdsfdsfdsfd"})}
EDIT AFTER COMMENTS:
You're asking why the example on the site is using a map. Look carefully at the value being used, it's a string
(client/post "url://site.com/api"
{:basic-auth ["user" "pass"]
:body "{\"json\": \"input\"}"
;; ...
The map is made up of lines of key/value pairs. The first is
key = :basic-auth, value = ["user" "pass"]
The value here is an array.
The second line is:
key = :body, value = "any old string"
In this case the string is an escaped map, the same that would be returned from calling json/write-str

What does this "#" mean in this Clojure context?

I stumbled upon the official Om example under "examples/mixins", there's a definition as this:
(def MyComponent (let [obj (om/specify-state-methods! (clj->js om/pure-methods))]
(aset obj "mixins" #js [TestMixin])
(js/React.createClass obj)))
What does "#js" mean? I couldn't find any Clojure documentation on symbol "#" when it's used in this way.
From the Om tutorial:
#js {...} and #js [...] is what is referred to as a reader literal. ClojureScript supports data literals for JavaScript via #js. #js {...} is for JavaScript objects:
#js {:foo "bar"} ;; is equivalent to
#js {"foo" "bar"}
First of all Om is a Clojurescript library. There are some differences between Clojure and Clojurescript, they are documented here.
#js is called a tagged literal, that comes from edn, basically a tagged literal tells the reader how to parse the data, in the case of #js it tells the reader to parse the data as a Javascript object. Take a look here for more information.
All dispatch reader macros begin with #. When you see this it means something special will happen at the moment the code is read, and that is no different with #js than with #( or #_ or even #{ for sets. In this case it tells the reader that what follows is a JavaScript literal.
More here: http://clojure.org/reader#The%20Reader--Macro%20characters

Convert to CSV value to Clojure list

What is the best way to turn this line of CSV for column 3 to a Clojure list?
357302041352401, 2012-08-27 19:59:32 -0700, 100, ["SNIA34", "M33KLC", "M34KLC", "W35REK", "SRBT", "MODE", "BFF21S", "CC12", "RCV56V", "NBA1", "RESP", "A0NTC", "PRNK", "WAYS", "HIRE", "BITE", "INGA1", "M32MOR", "TFT99W", "TBF5P", "NA3NR"]
Assuming you can already read the csv file...
You can use read-string in combination with into
user=> (def your_csv_column "[\"SNIA34\", \"M33KLC\", \"M34KLC\"]")
#'user/your_csv_column
user=> (into '() (read-string your_csv_column))
("M34KLC" "M33KLC" "SNIA34")
You can use Clojure Csv to do that.
You data is interesting, which appears to include a traditional comma-separated line, followed by data in brackets. I could not quite tell if the bracketed data was the representation you had in the .csv file or wanted after reading, but either way, this is how I read a .csv file:
My library's project.clj that uses clojure-csv:
(defproject util "1.0.4-SNAPSHOT"
:description "A general purposes Clojure library"
:dependencies [[org.clojure/clojure "1.4.0"]
[clojure-csv/clojure-csv "1.3.2"]]
:aot [util.core]
:omit-source true)
My library's core.clj header:
(ns util.core
^{:author "Charles M. Norton",
:doc "util is a Clojure utilities directory containing things
most Clojure programs need, like cli routines.
Created on April 4, 2012"}
(:require [clojure.string :as cstr])
(:import java.util.Date)
(:import java.io.File)
(:use clojure-csv.core))
My library's function that returns a .csv file parsed as a vector of vectors.
(defn ret-csv-data
"Returns a lazy sequence generated by parse-csv.
Uses open-file which will return a nil, if
there is an exception in opening fnam.
parse-csv called on non-nil file, and that
data is returned."
[fnam]
(let [ csv-file (open-file fnam)
inter-csv-data (if-not (nil? csv-file)
(parse-csv csv-file)
nil)
csv-data (vec (filter #(and pos? (count %) (not (nil? (rest %))))
inter-csv-data))]
;removes blank sequence at EOF.
(pop csv-data)))
(defn fetch-csv-data
"This function accepts a csv file name, and returns parsed csv data,
or returns nil if file is not present."
[csv-file]
(let [csv-data (ret-csv-data csv-file)]
csv-data))
What I have found to be very helpful is avoid using nth -- very useful advice from SO and other sources -- and given most of my .csv data is from database queries, I zipmap columns to each .csv seqeuence (row), and then operate on that data by map key. It simplifies things for me.

How do I persist and restore my defstruct's to a file?

I want to persist my data to a file and restore the data when I rerun the program.
I've defined my defstruct as such:
(defstruct bookmark :url :title :comments)
Program will simply do the following:
1. Load the defstruct's from url-db.txt
2. Read from an import file(s) passed into *command-line-args* and add to internal data var.
3. Rewrite the url-db.txt file.
Sample import file:
www.cnn.com|News|This is CNN
www.msnbc.com|Search|
news.ycombinator.com|News|Tech News
Tom Crayford's answer is close, but use the "pr" function instead of "print". "pr" produces strings that can be read back in with "read".
(defn save-db [db filename]
(spit
filename
(with-out-str (pr db))))
(defn load-db [filename]
(with-in-str (slurp filename)
(read)))
Note that this will not work if *print-dup* is set to true. See ticket #176 Note also that when you read the database back in, the records will be ordinary maps, not struct maps. Struct maps cannot yet be serialized with pr/read.
Use spit and slurp (example taken from http://www.nofluffjuststuff.com/blog/stuart_halloway/2008/09/pcl__clojure_chapter_3_1). Generally this technique is called serializing.
(defn save-db [db filename]
(spit
filename
(with-out-str (pr db))))
(defn load-db [filename]
(with-in-str (slurp filename)
(read)))
(The earlier print error was there in the original code, and I was dumb and didn't check it. Thanks)