I'm working with Noir and I can't figure out how to pass information to the views.
Right now I have a ref in proj.core/my-ref which is updated in a worker thread.
I need to access the ref's value from a view created via defpage located at proj.views.my-view.
What would be the idiomatic way of sharing this ref?
I was thinking of passing it in a closure somehow but I don't see how that would work with the way noir pulls in the views
ie
(noir.server/load-views-ns 'proj.views)
Move the ref to it's own namespace and then just require proj.core in your proj.views, like:
(ns proj.views
(:require proj.model))
(defpage "/foo" [] (#proj.model/my-ref)
Related
I have been using Clojure, ClojureScript, lein, shadow-cljs, Emacs, and CIDER to work on a Clojure/ClojureScript dynamic web app project.
Usually, I build the project executing command cider-jack-in-cljs in Emacs, choosing shadow-cljs, then shadow for REPL type, and, finally, app for building option.
It works fine. I can watch changes on the UI on localhost:3005.
There is something weird though that I do not understand (take into consideration that I am new to the Clojure stack).
On the REPL I can do:
cljs.user> #(re-frame.core/subscribe [:the-selections])
{:firm "cb08795f-378b-4eb0-9404-ad83b83a8474",
:active-client "Random-client",
:active-atb "c6193c35-bf91-4711-8523-d905bd7f0a03"}
The keywords inserted by me and retrieved by the REPL are related to the project.
My doubt is about the fact that this only works if the browser is in a specific page (address). Authentication here is not relevant.
On http://localhost:3005/link/random-numbers-asidadbsadkfbajhksdbf9283492374, it works:
cljs.user> #(re-frame.core/subscribe [:the-selections])
{:firm "cb08795f-378b-4eb0-9404-ad83b83a8474",
:active-client "Random-client",
:active-atb "c6193c35-bf91-4711-8523-d905bd7f0a03"}
But, if change the address bar on the Chrome browser to another valid path being properly rendered by the browser: http://localhost:3005/another-path.
And if try the same command, surprisingly the REPL retrieves nil:
cljs.user> #(re-frame.core/subscribe [:the-selections])
nil
Even after authentication and even if the browser address is inside the home page, the command above does not work. It only works after the address is on a specific page.
In addition, based on #ThomasHeller's comment, it seems to be relevant to post how :the-selections is defined. I am not sure this is the root of the definition, but it is my best bet:
(rf/reg-sub
:the-selections
;; Only returns something if all :firm, :active-client, and
;; :active-atb are present. :raw-selections defined in
;; selections.cljs omits validation if (rarely) needed. selections
;; are only stored locally in re-frame's app-db, not in firebase,
;; like :the-client and :the-atb. Minor selections components which
;; are not part of the validation are :active-account and :active-je
(fn [_q _d]
(rf/subscribe [:raw-selections]))
(fn [selections _q]
(tc-selections/valid-selections selections)))
This behavior intrigues me. Isn't a re-frame.core/subscribe an interface to the database?
According to re-frame's documentation:
When a View Function uses a subscription, like this (subscribe [:something :needed]), the sub-graph of nodes needed to service that subscription is created. The necessary sub-graph will "grow backwards" from the View Function all the way to app-db.
If so, why should the address bar on the browser matter after proper build and authentication?
This is impossible to answer without knowing how :the-selections is defined.
However, a guess would be that the value is provided by the "router". It basically takes the URL and puts some data related to it into the app-db. In case of URIs starting with /link it may be creating the necessary data, while others don't.
Devcards aims to provide a visual REPL experience for ClojureScript. They offer support to Reagent and OM. How can I make devCards work with re-frame?
This is a recurrent issue with re-frame and devcards. The main problem is the globals in re-frame (the main issue is the db, but handlers and subscriptions can be an issue too) that don't play well with the idea of rendering multiple devcards on the same page.
One potential solution is to render each devcard inside of an iframe. Each devcard would be isolated from each other, even though they are contained and visually rendered in a single page. It's probably not the most efficient solution, but it works: I implemented it in my devcards fork, under the iframe branch. It has a couple example devcards using re-frame
Even though it's published in clojars as [org.clojars.nberger/devcards "0.2.3-0-iframe"], it needs some work to provide a more friendly way to create iframe devcards and maybe a devcard macro specific for re-frame. Also there might be some UI rough edges to polish. But feel free to use it. Of course contributions and feedback are welcome.
I'll put an example here to show how to use it:
(defcard-rg re-frame-component-initialize-db
"This is the same re-frame component, but now using
data-atom to initialize the db, rendered in an iframe:"
(fn [data-atom _]
(setup-example-1)
(re-frame/dispatch [:initialize-db #data-atom])
[re-frame-component-example])
{:guest-name "John"}
{:iframe true})
(the example is based on re-frame 0.7.x but everything should work the same with newer versions because the iframe mechanism is indifferent to using re-frame or anything)
Update:
This how I did it with figwheel main:
Add [devcards "0.2.6" ] to your dependencies.
Create a namespace for your cards, say src/cljs/cards/core.cljs.
Add new extra-main-files section and turn devcards on in dev.cljs.edn
^{:watch-dirs ["src/cljs" "test"]
:css-dirs ["resources/public/css"]
:auto-testing true
:extra-main-files {:testing {:main menu-planner.test-runner}
:devcards {:main cards.core}} ;; <- this
:open-url false}
{:main menu-planner.core
:infer-externs true
:devcards true ;; <- and this
}
Add cards with defcard-rg to src/cljs/cards/core.cljs
(ns cards.core
(:require
[devcards.core]
[re-frame.core :as re-frame])
(:require-macros
[devcards.core :refer [defcard-rg]]))
(devcards.core/start-devcard-ui!)
(defcard-rg no-state
[:div {:style {:border "10px solid blue" :padding "20px"}}
[:h1 "Composing Reagent Hiccup on the fly"]
[:p "adding arbitrary hiccup"]])
(defcard-rg with-state
(fn [data-atom _]
[:div "test"])
{:initial-data "Ta-ta!"})
Run figwheel-main with the forementioned profile (dev).
Go to devcards
They say you can't at the first page:
re-frame remains a work in progress and it falls short in a couple of
ways - for example it doesn't work as well as we'd like with devcards
(defn my-func [opts]
(assoc opts :something :else))
What i want to be able to do, is serialize a reference to the function (maybe via #'my-func ?) to a string in such a way that i can upon deserializing it, invoke it with args.
How does this work?
Edit-- Why This is Not a Duplicate
The other question asked how to serialize a function body-- the entire function code. I am not asking how to do that. I am asking how to serialize a reference.
Imagine a cluster of servers all running the same jar, attached to a MQ. The MQ pubs in fn-reference and fn-args for functions in the jar, and the server in the cluster runs it and acks it. That's what i'm trying to do-- not pass function bodies around.
In some ways, this is like building a "serverless" engine in clojure.
Weirdly, a commit for serializing var identity was just added to Clojure yesterday: https://github.com/clojure/clojure/commit/a26dfc1390c53ca10dba750b8d5e6b93e846c067
So as of the latest master snapshot version, you can serialize a Var (like #'clojure.core/conj) and deserialize it on another JVM with access to the same loaded code, and invoke it.
(import [java.io File FileOutputStream FileInputStream ObjectOutputStream ObjectInputStream])
(defn write-obj [o f]
(let [oos (ObjectOutputStream. (FileOutputStream. (File. f)))]
(.writeObject oos o)
(.close oos)))
(defn read-obj [f]
(let [ois (ObjectInputStream. (FileInputStream. (File. f)))
o (.readObject ois)]
(.close ois)
o))
;; in one JVM
(write-obj #'clojure.core/conj "var.ser")
;; in another JVM
(read-obj "var.ser")
As suggested on the comments, if you can just serialize a keyword label for the function and store/retrieve that, you are finished.
If you need to transmit the function from one place to another, you essentially need to send the function source code as a string and then have it compiled via eval on the other end. This is what Datomic does when a Database Function is stored in the DB and automatically run by Datomic for any new additions/changes to the DB (these can perform automatic data validation, for example). See:
http://docs.datomic.com/database-functions.html
http://docs.datomic.com/clojure/index.html#datomic.api/function
As similar technique is used in the book Clojure in Action (1st Edition) for the distributed compute engine example using RabbitMQ.
I'm new to Clojure, and trying to get a few simple web routes set up. I want the routes to reload all associated code in development, but not in production.
I was only able to get this to work using the var's for the routes, not the actual symbols. Can someone explain if I'm doing this wrong? If not, why is the var required?
(def app-handler
(let [formats [:json-kw :edn :yaml-kw :yaml-in-html :transit-json :transit-msgpack]
wrapped-api (wrap-restful-format #'routes/api-routes :formats formats)
combined-routes (compojure.core/routes wrapped-api #'routes/html-routes)
with-defaults (wrap-defaults combined-routes api-defaults)]
(if (is-dev?)
; Development
(wrap-reload with-defaults)
; Production
with-defaults)))
(Note #'routes/api-routes and #'routes/html-routes above).
In a manner described in more detail in another answer, the server ends up capturing your route functions when they are passed in, and if you provide the var, this will ensure that the server uses any updated definitions.
This is considered the normal way to provide your route or handler function during development, so that you can see updated definitions without having to restart your web server process.
I was looking at https://github.com/juxt/dirwatch library. The example from the front page is:
(require '[juxt.dirwatch :refer (watch-dir)])
(watch-dir println (clojure.java.io/file "/tmp"))
That works fine. Let's say the above is executed in REPL:
user=> (watch-dir println (clojure.java.io/file "/tmp"))
#<Agent#16824c93: #<LinuxWatchService sun.nio.fs.LinuxWatchService#17ece9ac>>
Now, I have an agent that will print events when I modify files in /tmp:
{:file #<File /tmp/1>, :count 1, :action :modify}
so all is fine.
I know I can reference the agent by using previous expression references (*1, *2 and *3). However, I don't know how to, without restarting the REPL itself:
Unbind an implicit var created like this - i.e. how to remove the binding completely, so that agent gets GCed and stops working
Access it in case I lost it in cases where I did not bind it, such as the above. If I'm not mistaken, in REPL only the last three results are available (*3 is, but *4 and further are not), at least per http://clojure.org/repl_and_main
Any suggestions?
Did you take a look at the code? The documentation to watch-dir has this: "The watcher returned by this function is a resource which
should be closed with close-watcher."
Looking at the code, it watch-dir uses send-off, which "Dispatch a potentially blocking action to an agent. Returns the agent immediately.". In other words, to address your first question, there is no implicit var created. If you want to get rid of the agent, you should bind the returned agent to some var and call close-watcher on it afterwards.
To address the second question, take a look at the canonical documentation for agents. Specifically, you can call shutdown-agents, which will shut-down the thread pool (potentially killing other agents as well).