Figwheel doesn't detect most changes in my code - clojure

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.

Related

with-redefs not replacing valid Compojure route function call in clojure.test

I want to test my Compojure endpoints and see that the required method is called. However, with-redefs-fn is not replacing the method.
Test method-
(ns project-name.handler-test
(:require [clojure.test :refer :all]
[project-name.handler :as handler :refer [retrieve-records test-handler]]
[ring.mock.request :as mock]))
(deftest fn-handler-routes
(with-redefs-fn {#'handler/retrieve-records (fn [arg] :test)}
(let [response {:status 200 :body :test}]
(let [mock-handler (test-handler)]
(is (= response (mock-handler (mock/request :get "/records/name"))))))))
I am guessing routes are static, and thus the method fires and returns actual data instead of calling the redefined function. test-handler is a failing attempt to try to circumvent -- it fires okay, but does not use with-redefs and returns repo data.
Pertinent Source Code-
(defn retrieve-records [arg] (repo/get-records arg))
(defroutes routes
(GET "/" [] (resource-response "index.html" {:root "public"}))
(context "/records" []
(GET "/name" [] (retrieve-records :lastname)))
(resources "/"))
(defn test-handler []
(-> #'routes wrap-reload))
Related Issues
I suspect a similar underlying issue as this SO question about Midje but I am using clojure.test and not midje.
This question is different from a very similar SO question in that the Compojure route is legitimate and I want to return something in the :body, not just test the status (already tried that answer).
This question is different from an integration test question in that it is a handler unit test, though probably the same underlying issue.
Any help would be greatly appreciated.

Using macros to generate Om components

I'm attempting to use macros to generate a series of similar Om components (e.g. a modal element which contains common boilerplate and a dynamic "body").
I've got the solution below mostly working. The one exception is accessing the appropriate owner in the form's on-submit event handler.
;; macros.clj
(defmacro build-modal
([disp-name body]
`(fn [_# owner#]
(reify
om.core/IRender
(~'render [_#]
(dom/div {:class "login-view-modal"}
(dom/div {:class "login-view-modal-backsplash"})
(dom/div {:class "login-view-modal-content"}
~#body))))) nil))
;; ui.cljs
(defn login-view-modal []
(bs-macros/build-modal
"login-modal"
'(dom/form {:on-submit (fn [event]
(.preventDefault event)
(let [username (get-value owner "username")
password (get-value owner "password")
data {:email_address username
:password password}]
(handle-login-view-modal-form-submit data)))}
;; elided for brevity
(dom/div
(dom/button "LOG IN")))))
(defcomponent home-page-view [app owner]
(init-state [_] {:text ""})
(render-state [this state]
;; elided for brevity
(dom/div (login-view-modal))))
The modal is rendered as expected, however when submitting the form I'm presented with the error: Uncaught TypeError: Cannot read property 'getDOMNode' of undefined, which seems to be because owner is not in scope. (Note, this element and its event handler functions as expected when constructed in full - i.e. without using a macro.)
How do I make the appropriate owner available to the body of the macro?
You need variable capture for this to work. Instead of gen-syming via owner# which will generate a unique symbol, just inline ~'owner wherever you want to refer to it.

Dynamic handler update in Clojure Ring/Compojure REPL

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"

What is the difference between using defmulti and defn as a function of om/build-all?

om "0.8.0"
I have recently started learning om by using examples codes of om repository. Now, I am checking multi example and I can understand the behavior of this program.
After I clicked '+' button,
First, "Even(or Odd) widget unmounting" is printed.
Next, "Odd(or Even) widget mounting" is printed.
But when I added following code
(just change even-odd-widget defmulti code to defn code)
(defn test-widget
[props owner]
(reify
om/IWillMount
(will-mount [_]
(println "Test widget mounting"))
om/IWillUnmount
(will-unmount [_]
(println "Test widget unmounting"))
om/IRender
(render [_]
(dom/div nil
(dom/h2 nil (str "Test Widget: " (:my-number props)))
(dom/p nil (:text props))
(dom/button
#js {:onClick #(om/transact! props :my-number inc)}
"+")))))
and tried to use this function instead of test-widget, as the result, there was no print message...
So what is the difference between defmulti and defn in this case?
Is this bug or correct behavior?
Thanks in advance.
The protocol methods om/IWillMount and om/IWillUnmount are only invoked once when the component is mounted/unmounted. It is not invoked on every render and therefore a log message will not be produced when you click the '+' button if it does not result in a component being mounted/unmounted. The use of multimethods has no impact on this behavior.
The reason that you are seeing repeated log statements when using the multimethod version is that different components are being returned on every click and therefore the components are also mounted/unmounted every time you click '+', while with the plain function a single component is mounted and remains mounted every time you click '+'.
The log messages you are producing will appear in the developer console of the browser for both your plain function and multimethod versions, but only when the component mounts/umounts and not on every render.
Clicking the '+' button will trigger a re-render in the existing mounted component in the plain function scenario. If you want to log on every render of the component, you would need to do the following:
(defn test-widget
[props owner]
(reify
om/IWillMount
(will-mount [_]
(println "Test widget mounting"))
om/IWillUnmount
(will-unmount [_]
(println "Test widget unmounting"))
om/IRender
(render [_]
(.log js/console "Rendering test widget!")
(dom/div nil
(dom/h2 nil (str "Test Widget: " (:my-number props)))
(dom/p nil (:text props))
(dom/button
#js {:onClick #(om/transact! props :my-number inc)}
"+")))))
(defn app [props owner]
(reify
om/IRender
(render [_]
(apply dom/div nil
(om/build-all test-widget (:widgets props))))))

Testing Luminous rendering in clojure with core.test

This code is from the default luminus template:
(deftype RenderableTemplate [template params]
Renderable
(render
[this request]
(content-type
(->>
(assoc
params
(keyword (s/replace template #".html" "-selected"))
"active"
:servlet-context
(:context request)
:user-id
(session/get :user-id)
:user
(session/get :user))
(parser/render-file (str template-path template))
response)
"text/html; charset=utf-8")))
(defn render [template & [params]]
(RenderableTemplate. template params))
And I need to test this function using clojure.test:
(defn home-page [& [user]]
(layout/render
"home.html"
{:user user}))
How will I test the above function with the value associated to the key :user?
I suggest you to read some documentation and make a reasonable effort before launching so general questions. This could be an start http://blog.jayfields.com/2010/08/clojuretest-introduction.html
After you feel a bit comfortable with clojure test you may like to move to https://github.com/marick/Midje/wiki/A-tutorial-introduction-for-Clojure.test-users
Enjoy :)
First set your routes and handlers like this:
(defn home-page [& [user]]
(layout/render
"home.html"
{:user user}))
; setting routes
(defroutes main-routes
(GET "/user/:user" [user] (home-page user)))
Then do the unit testing:
Basic unit test
(deftest home-page-test
; Check if response code is 200
(is (= 200 (:status (main-routes {:request-method :get :uri "/user/Michael"})))))
or you can also use Midje
Using Midje
(:use midje.sweet
clojure.test)
(fact "Homepage Test"
(:status (main-routes {:request-method :get :uri "/user/Michael")) => 200)
I faced the same problem.
Calling (home-page) directly will return a RenderableTemplate type which isn't useful for testing.
In your test require:
(:require [capacityplanning.layout :as layout]
[selmer.parser :as parser])
Add this function:
(def template-path "templates/")
(defn mockRender [template params]
(parser/render-file (str template-path template) params))
And in your test you can bind:
(with-redefs [layout/render mockRender]
(home-page user))
Now when home-page is called it will return the html as a string. This should be more useful to write unit tests against.