I learned that wrap-reload from ring needs to capture the var itself not the value, but what if my value is dynamically generated and not a top level var?
(defn -main [options]
(let [app (make-app options)]
;; This won't work either:
;; (run-jetty (wrap-reload #'app))
(run-jetty (wrap-reload app))
))
Clojure let bindings do not create a Var object, so you cannot use the trick of passing (var app) (or it's shortcut #'app) in place of the function object that app points to.
Please see the following for more detail: When to use a Var instead of a function?
Related
Figwheel displays the code bellow just fine. But I have to refresh the page to see any changes. What has to change for Figwheel to show changes? Is there a command to force redraw, without losing the application state?
BTW: Chrome has Disable Cache true and the CLJS icon appears when the file is saved.
(defn simple-example []
[ui/mui-theme-provider {:mui-theme (get-mui-theme
{:palette {:text-color (color :blue800)}})}
[:div
[ui/app-bar {:title "Hi all"
:icon-class-name-right "muidocs-icon-navigation-expand-more"}]
[ui/paper
[:div
[ui/tabs
[ui/tab {:label "Menu" :value "0"}
[:div "Hello world"]]]]]]]))
(defn ^:export run []
(render [simple-example]
(js/document.getElementById "app"))))
From the docs:
Setting :figwheel true or :figwheel { :on-jsload "example.core/reload-hook" } will automagically insert the figwheel client code into your application. If you supply :on-jsload the name of a function, that function will be called after new code gets reloaded.
An example reload hook plus configuration for Reagent:
(ns your-namespace.core
(:require [reagent.core :as r]))
(defn render [view]
(let [node (.getElementById js/document "app")]
(r/render-component view node)))
(defn rerender []
(let [node (.getElementById js/document "app")]
(r/unmount-component-at-node node)
(render [:div "Reloading"]))
(defn ^:export reload []
(rerender))
And then in your project.clj:
:cljsbuild {:builds {:dev {:source-paths ["src"]
:figwheel {:on-jsload "your-namespace.core/reload"}}}
/edit
Note that re-frame uses Reagent. In the case of re-frame I recommend starting with the re-frame-template. E.g.,
lein new re-frame your-project-name # options, e.g., +re-frisk +cider
This will give a default core.cljs as follows:
(defn dev-setup []
(when config/debug?
(enable-console-print!)
(println "dev mode")))
(defn mount-root []
(re-frame/clear-subscription-cache!)
(reagent/render [views/main-panel]
(.getElementById js/document "app")))
(defn ^:export init []
(re-frame/dispatch-sync [:initialize-db])
(dev-setup)
(mount-root))
The index.html has a node with id app and calls init. And the project.cljs specifies the on-jsload as follows:
:cljsbuild
{:builds
[{:id "dev"
:source-paths ["src/cljs"]
:figwheel {:on-jsload "your-project-name.core/mount-root"}
#_(...)}}
This should absolutely update the page with the component changed. If it does not do what you want I might have misunderstood your question.
Reagent needs to be notified about state changes to re-render the affected components on the screen. Your code does not yet have any inner state that can be watched in order to decide if a re-render is required.
You can store your app state in reagent atoms. When you dedreference a reagent atom in a reagent component (that is the simple-example component in your case) an event listener is set up to the state atom so that any time it changes the component will be re-rendered.
Put the following just before the definition of simple-example:
(defonce counter (reagent.core/atom 0))
(swap! counter inc)
This creates a state called counter if it does not exist yet. It also immediately increases it so any already registered components will be refreshed.
Then put a #counter deref call anywhere inside the function body of simple-example. This way the initial call of the function installs the state change listeners.
Now any time you modify the code the namespace get reloaded and thus counter atom increases triggering the re-render of your component.
I am using Clojure and Monger
It works fine, and I group functions by the collection they relate to.
Therefore, every file begins like this :
(ns img-cli.model.mycollectionname
(:require [monger.core :as mg]
[monger.collection :as mc]
[edn-config.core :refer [env]])
(:import [com.mongodb MongoOptions ServerAddress DB WriteConcern]
[org.bson.types ObjectId]))
(def config (get-in env [:mongo]))
;; using MongoOptions allows fine-tuning connection parameters,
;; like automatic reconnection (highly recommended for production
;; environment)
(def ^MongoOptions opts (mg/mongo-options { :threads-allowed-to-block-for-connection-multiplier 300}))
(def ^ServerAddress sa (mg/server-address (:url config) (:port config)))
(def conn (mg/connect sa opts))
(def db (mg/get-db conn (:db config)))
(def collection-name "asset")
;; I have to write one like this every time
(defn find-one-as-map
"fetch asset by Id"
[^String id]
(mc/find-one-as-map db collection-name {:_id (ObjectId. id)}))
Code duplication has of course several disadvantages in itself.
Also I'm not sure if connections are properly pooled afterwards ?
How can I avoid doing this ?
I sense I could pass an additional "db" parameter to each function, but then where would it come from ?
If I create the db connection in the "entry" file of my program, then how could it be passed to every function from there ?
For instance let's says I have Compojure routes in different files :
;; in the main handler file
(def db ...) ;; if I move the previous db configuration
;; in here, it could be the only place where this is set
;; importing Compojure routes from different files
(defroutes routes-from-file1
routes-from-file2...)
Let's say that some functions called from some of the routes in "file2" need access to the db, how can I pass this variable to them ?
I also have a lot of repetitive code afterwards, for instance to get data by Id for every collection...
I feel this could be simplified, but I'm not sure how.
Just refer to it by its namespace
(ns foo
(:require [handler :as h]))
(println h/db)
Here is what i am trying to do, I can replace Dom with jQuery, but I don't know how to get the Dom value. That's shame.
Gist
This is a gist which I followed it.
So I ask the origin author, but no response yet.
PS: I just want to try some cljs in browser, so I didn't use any lib like jayq.
I have tried something like (.val "yes"), but it seems to be wrong.
(ns hello-world.jquerytest)
(enable-console-print!)
(def jquery (js* "$"))
(defn x []
(->
(jquery ".meat")
(.html "yes")))
(jquery
(fn []
(x)
(-> (jquery "li.numbers")
(.html "pink")
(.append "banana"))))
Here is my answer based on Vanessa's comment
I tried
(defn x []
(->
(jquery ".meat")
(.html)
(println)))
It will print the value which I want.
If I am using doto
It will print #<[object Object]> in console which I do not want.
(defn x []
(->
(doto
(jquery ".meat")
(.html)
(println))))
I've created a new Compojure Leiningen project using lein new compojure test. Web server is run by lein repl and then
user=> (use 'ring.adapter.jetty)
user=> (run-jetty test.handler/app {:port 3000})
Routes and app handler specification is trivial:
(defroutes app-routes
(GET "/*.do" [] "Dynamic page")
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
Now, after changing anything in app-routes definition (e.g. changing "Dynamic page" text to anything else, or modifying URI matching string), i do not get the updated text/routes in the browser. But, when changing app-routes definition slightly to
(defn dynfn [] "Dynamic page fn")
(defroutes app-routes
(GET "/*.do" [] (dynfn))
(route/not-found "Not Found"))
i do get dynamic updates when changing the return value of dynfn. Also, following the advice from this article and modifying the app definition to
(def app
(wrap-defaults #'app-routes site-defaults))
(note the #' that transparently creates a var for app-routes) also helps!
Why is that so? Is there any other way one could get a truly dynamic behaviour in defroutes?
Thanks!
#'app-routes is a reader macro that expands to (var app-routes). When a var is used as if it were a function, it is dereferenced anew on each invocation, and then the value returned by that deref is called.
If you were to supply app-routes as the argument, the compiler would give the dereferenced value to wrap-defaults, and when the var is updated, the previous value is not changed, so changing the var does not change the behavior of app.
The following repl transcript might be instructive:
user=> (defn foo [] "original")
#'user/foo
user=> (defn caller [f] #(f))
#'user/caller
user=> (def call-foo-value (caller foo))
#'user/call-foo-value
user=> (call-foo-value)
"original"
user=> (def call-foo-var (caller #'foo))
#'user/call-foo-var
user=> (call-foo-var)
"original"
user=> (defn foo [] "changed")
#'user/foo
user=> (call-foo-value)
"original"
user=> (call-foo-var)
"changed"
I'm attempting to use Midje to stub the view in a handler unit test, but my use of Midje's (provided) obviously isn't correct.
I've simplified and inlined the view to a (content) function in the handler:
(ns whattodo.handler
(:use compojure.core)
(:require [whattodo.views :as views]))
(defn content [] (views/index))
(defn index [] (content))
(defroutes app
(GET "/" [] (index)))
and am trying to test it using
(ns whattodo.t-handler
(:use midje.sweet)
(:use ring.mock.request)
(:use whattodo.handler))
(facts "It returns response"
(let [response (app (request :get "/"))]
(fact "renders index view" (:body response) => "fake-html"
(provided (#'whattodo.handler/content) => (fn [] "fake-html")))))
I was expecting the stubbed function to be called returning 'fake-html' and thus the unit test passing, but instead, the test fails as the real implementation is called - invoking the real view.
You don't need the function shortcut, just use (content) => .... As you have it right now, midje expects that your code calls literally (#content), but your index function calls (content) instead. Your confusion about midje's syntax might be that you expect that you assign to the function name the expected result, but that is not the case. You have to replace the exact call. I.e., if your index function would call content with some argument, you would have to account for this as well, e.g. by (provided (content "my content") => ...)
I discovered today that I had my scopes confused - the let block introducing response was outside of the fact call that included the provided. Thus the response was created before the provided was invoked.
Working code which passed this early test instead used the against-background call
(facts "It returns response"
(against-background (whattodo.handler/content) => "fake-html")
(let [response (app (request :get "/"))]
(fact "renders index view"
(:body response) => "fake-html")))