When I eval this code in lighttable:
(ns app.core
(:require [datomic.api :refer [q] :as d]
:reload-all))
(defn add-person
[conn id]
(d/transact conn [{:db/id #db/id[:db.part/user -1000001]
:person/id id}]))
I get:
clojure.lang.ExceptionInfo: No reader function for tag id
core.clj:4327 clojure.core/ex-info
Anyone knows what is going on?
This tutorial is attributed to stuart halloway and Bobby Calderwood:
(use :reload 'datomic.samples.repl)
(easy!)
(def conn (scratch-conn))
;; in data, use data literals for tempids
(def tx-data [{:db/id #db/id[:db.part/user]
:db/doc "Example 1"}])
(transact conn tx-data)
;; in code, call tempid to create tempids
(let [id (tempid :db.part/user)
doc "Example 2"]
(transact conn [{:db/id id :db/doc doc}]))
;; same argument applies to functions:
;; use #db/fn literals in data
;; use Peer.function or d/function in code
;; broken, uses db/fn literal in code
(transact conn [{:db/id #db/id [:db.part/user]
:db/ident :hello
:db/fn #db/fn {:lang "clojure"
:params []
:code '(println :hello)}}])
;; corrected: used d/function to construct function
(transact conn [{:db/id (d/tempid :db.part/user)
:db/ident :hello
:db/fn (d/function {:lang "clojure"
:params []
:code '(println :hello)})}])
(d/invoke (db conn) :hello)
Source: https://github.com/Datomic/day-of-datomic/blob/master/samples/literals_vs_code.clj
It looks like there's an issue with trying to set :person/id. After the #db/id[:db.part/user -1000001] part, you've got a temporary id for adding data.
You should be able to start setting attributes for the entity using things like things like :person/name name.
If you're trying to create a "public id" type of thing, this blog post may be helpful.
It's a problem in nREPL. The way I solved this is to start the REPL at the command line with:
lein repl
This will start a process that you can connect to from LightTable or Emacs. It will print information like:
nREPL server started on port 51395 on host 127.0.0.1
^^^^^
Now in LightTable, Add a Connection -> Clojure Remote -> 127.0.0.1:XXXXX
The XXXXX should equal the port printed out by lein repl.
If you're in Emacs, cider has the same issue. Follow the same steps of starting lein repl, then use M-x cider-connect (it's default keybinding is C-c M-c).
Related
I made service endpoint api for getting single object by id and it works as expected. I tested it with Postman and in handler function. I use cljs-ajax library for asynchronous client. I cant change the state of Reagent atom when I get response. Here is the code:
(ns businesspartners.core
(:require [reagent.core :as r]
[ajax.core :refer [GET POST]]
[clojure.string :as string]))
(def business-partner (r/atom nil))
(defn get-partner-by-id [id]
(GET "/api/get-partner-by-id"
{:headers {"Accept" "application/transit+json"}
:params {:id id}
:handler #(reset! business-partner (:business-partner %))}))
When I tried to access business-partner atom I got nil value for that atom. I can't figure out why because another method is almost the same except it get's list of business partners and works fine.
When I change the get-partner-by-id function:
(defn get-partner-by-id [id]
(GET "/api/get-partner-by-id"
{:headers {"Accept" "application/transit+json"}
:params {:id id}
:handler (fn [arg]
(println :handler-arg arg)
(reset! business-partner (:business-partner arg))
(println "Business partner from handler: " #business-partner))}))
Output in the browser console:
:handler-arg {:_id 5e7ad2c84b5c2d44583e8ecd,
:address Main Street,
:email nenmit#gmail.com,
:phone 555888,
:name Nen Mit}
Business partner from handler: nil
So, as you can see, I have my object in handler as desired, but when I try to reset my atom nothing happens. That's the core of the problem I think. Thank you Alan.
When in doubt, use debug print statements. Make your handler look like this:
:handler (fn [arg]
(println :handler-arg arg)
(reset! business-partner (:business-partner arg)))
You may also want to use clojure.pprint/pprint to pretty-print the output, or also add (type arg) to the output.
You may also want to initialize the atom to a specific value like
:bp-default so you can see if the nil you observe is the original one or if it is being reset to nil.
Update
So it is clear the key :business-partner does not exist in the map you are receiveing. This is what you must debug.
Trying to pull a non-existent key out of a map always returns nil. You could also use the 3-arg version of get to make this explicit. Convert
(:business-partner arg) => (get arg :business-partner ::not-found)
and you'll see the keyword ::not-found appear in your atom, verifying what is occurring.
In order to catch these problems early, I nearly always use a simple function grab from the Tupelo library like so:
(:business-partner arg) => (grab :business-partner arg)
The grab function will throw an exception if the expected key is not found. This provides early-warning of problems so you can track them down faster.
Another hint: next time use prn instead of println and it will retain double-quotes on string output like:
"Main Street"
I'm having some trouble to get started with Light Table.
Here's my code (Clojure)
(ns prova1-ed.core
(:gen-class))
(use 'clojure.java.io)
(defn -main [& args]
(println "Type the name of the file to read: ")
(let [fileName (read-line)]
(let [rdr (reader fileName)]
(doseq [line (line-seq rdr)]
(println line)
)))
)
I'm sure it works. I've tested with lein run. As you can see, the program should read a file which the name is given by the user.
I've tried CTRL+SPACE in Light Table, but this is what I receive:
ERROR: Unhandled REPL handler exception processing message {:data {:auto? false, :pos {:line 14, :ch 1}, :mime "text/x-clojure", :tags [:editor.clj :editor.clojure], :type-name "Clojure", :line-ending "\r\n", :ns user, :path "C:\\Users\\Tiago\\Documents\\Clojure\\prova1_ed\\src\\prova1_ed\\core.clj", :print-length nil, :name "core.clj", :local true, :code "(ns prova1-ed.core\n (:gen-class))\n\n(use 'clojure.java.io)\n\n(defn -main [& args]\n\n (println \"Type the name of the file to read: \")\n\n (let [fileName (read-line)]\n (let [rdr (reader fileName)]\n (doseq [line (line-seq rdr)]\n (println line)\n )))\n)\n"}, :id 90, :op editor.eval.clj.sonar, :session 65d1da68-a730-4ffe-9365-9527726384e3}
How can i run it in the Light Tables' enviroment, so that I can input the file name?
TLDR
I don't think you can run (read-line) in Light Table as it'd have to add explicit support for allowing input. There's no standard input basically.
An Alternative
I'd suggest you modify your -main function to accept an explicit file-name argument instead of trying to read it from a standard input that isn't available.
I've got a Clojure webapp that I work on in Light Table.
I've got a -main function in a namespace named my-app.web. It looks something like this:
(defn -main [& [port]]
(let [port (Integer. (or port (env :port) 5000))
store (cookie/cookie-store {:key (env :session-secret)})]
(jetty/run-jetty (-> #'secured-app
wrap-with-logging
wrap-current-user
wrap-current-auth
wrap-error-page
(site {:session {:store store}}))
{:port port :join? false})))
In a separate file I've named light-table-start.clj, I've got the following code to run my app inside Light Table:
(require '[my-app.web :as web])
(require '[ring.adapter.jetty :as jetty])
(defonce server (web/-main "5000"))
;; (.start server)
;; (.stop server)
I run the Eval: Eval editor contents command (Ctrl+Shift+Enter on Windows and Linux or ⌘+Shift+Enter on Mac OS) the first time I want to run my app (or later, if the connection is closed for some reason). When I want to start or stop the server I can just highlight the code on the respective commented lines and run the Eval: Eval a form in editor command (Ctrl+Enter on Windows and Linux or ⌘+Enter on Mac OS).
On a html-post page a user can input various fields and hit submit,
My router.clj code looks like
(POST "/postGO" [ post-title post-input post-tags :as request ]
(def email (get-in request [:session :ze-auth-email]))
;; connect to datomic and write in the request
(dbm/add-ze-post post-title post-input post-tags email) ;; db insert
{:status 200,
:body "successfully added the post to the database",
:headers {"Content-Type" "text/plain"}}) ;;generic return page
It works well, but I want to redirect the user afterwards to a page that can show them their uploaded post. To do this, it would be very helpful to have the eid of the entity just transacted.
;; code from my dbm file for playing directly with the database
;; id est: the db transact code
(defn add-ze-blurb [title, content, tags, useremail]
(d/transact conn [{:db/id (d/tempid :db.part/user),
:post/title title,
:post/content content,
:post/tag tags,
:author/email useremail}]))
Is there any way to have datomic return the eid as soon as something is added to the DB successfully, or should I use another query right afterward to make sure it's there?
For simple cases, simply deref the future returned by d/transact and use :tx-data. Full example:
(require '[datomic.api :refer [db q] :as d])
(def uri "datomic:mem://test")
(d/create-database uri)
(def conn (d/connect uri))
(def schema [{:db/id (d/tempid :db.part/db)
:db/ident :foo
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}])
#(d/transact conn schema)
(def tempid (d/tempid :db.part/user))
(def tx [{:db/id tempid :foo "bar"}])
(def result #(d/transact conn tx)) ;;
(def eid (:e (second (:tx-data result))))
(assert (= "bar" (:foo (d/entity (db conn) eid))))
You can alternatively use d/resolve-tempid:
(def eid (d/resolve-tempid (db conn) (:tempids result) tempid))
(assert (= "bar" (:foo (d/entity (db conn) eid))))
As described in the Datomic documentation, after the deref the transaction will have been applied by the transactor and the data returned reflects the new value of the database.
I am getting a FileNotFoundException when using a database function that requires a namespace. I only get the error when using the persistent datomic free database but not when I'm using the memory database.
(ns test.core
(:use [datomic.api :only [q db] :as d]))
(def uris ["datomic:mem://test"
"datomic:free://localhost:4334/test"])
(map
d/delete-database uris)
(map
d/create-database uris)
(def conns (map d/connect uris))
(defn test-entity []
[{:db/id #db/id[:db.part/db]
:test/test "hello"}])
(def db-function
#db/fn {:lang :clojure
:params [database]
:requires [[test.core :as c]]
:code (c/test-entity)})
(map
#(d/transact % [{:db/id #db/id[:db.part/user]
:db/ident :db-function
:db/fn db-function}])
conns)
(map
#(d/transact % [{:db/id #db/id[:db.part/db]
:db/ident :test/test
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}])
conns)
(comment
(db-function nil)
(d/transact (first conns) [[:db-function]])
(d/transact (second conns) [[:db-function]]))
When you evaluate the first and second line in the comment, it's fine but when you evaluate the third line you get an exception.
Do I need to configure something in datomic so that it can "see" my project?
When you use an in memory database, the transactor runs in the same JVM instance as the peer, hence with the same classpath. But with the free database, the transactor is running in its own JVM instance, and is not aware about namespaces in the peers.
You can add jars to the transactor classpath by putting them in the lib/ folder.
After I run a transaction in datomic to insert a value, how I can use the return value of the transaction to get the ids of any entities that were created?
Here is a sample of the return value I get after an insert:
#<promise$settable_future$reify__4841#7c92b2e9: {:db-before datomic.db.Db#62d0401f, :db-after datomic.db.Db#bba61dfc,
:tx-data [#Datum{:e 13194139534331 :a 50
:v #inst "2013-06-19T11:38:08.025-00:00"
:tx 13194139534331 :added true} #Datum{:e 17592186045436 .....
I can see the underlying datums...how can I extract their values?
Use d/resolve-tempid. If you were to transact a single entity, looking at :tx-data would work but if your transaction contained more than one entity, then you wouldn't know the order in which they appear in :tx-data.
What you should do is give temporary ids to your entities (before transacting them) using either (d/tempid) or its literal representation #db/id[:db.part/user _negativeId_] and then use d/resolve-tempid to go from your temporary id to the real id given by the database. The code would look something like:
(d/resolve-tempid (d/db conn) (:tempids tx) (d/tempid :db.part/user _negativeId_))
For a full code sample, see this gist.
Ah, figured it out.
I had to deref the Clojure promise, and then I was able to yank out the values I wanted:
(:e (second (:tx-data #(transact! conn query))))
Wrote a quick function based on a2ndrade's answer. The naming isn't ideal and I may be committing idiomatic faux pas; suggestions are very much welcome.
(ns my.datomic.util
(:require [datomic.api :as d]))
(defn transact-and-get-id
"Transact tx and return entity id."
[conn tx]
(let [tempid (:db/id tx)
post-tx #(d/transact conn [tx])
db (:db-after post-tx)
entid (d/resolve-tempid db (:tempids post-tx) tempid)]
entid))
Example usage:
(def my-conn
(d/connect (str "datomic:sql://datomic?jdbc:postgresql://"
"127.0.1:5432/datomic?user=datomic&password=somepw")
(defn thing-tx
"Create transaction for new thing."
[name]
{:db/id (d/tempid :db.part/user)
:thing/name name})
(transact-and-get-id my-conn (thing-tx "Bob")) ;; => 17592186045502
The Tupelo Datomic library has a function (td/eids tx-result) to easily extract the EIDs created in a transaction. For example:
; Create Honey Rider and add her to the :people partition
(let [tx-result #(td/transact *conn*
(td/new-entity :people ; <- partition is first arg (optional) to td/new-entity
{ :person/name "Honey Rider" :location "Caribbean" :weapon/type #{:weapon/knife} } ))
[honey-eid] (td/eids tx-result) ; retrieve Honey Rider's EID from the seq (destructuring)
]