tolitius / mount

managing Clojure and ClojureScript app state since (reset)
Eclipse Public License 1.0
1.22k stars 88 forks source link

def doesn't work with mount #77

Closed kwladyka closed 1 year ago

kwladyka commented 7 years ago
(mount/defstate foo
                :start (println "!!! start")
                :stop (println "!!! stop"))

(def app foo)

(mount/start)

Then app throw an error mount.core.DerefableState cannot be cast to clojure.lang.IFn. This is typical situation when you use ring and have app and routes definition. This is how i get this situation.

It tooks me some time to discover it. If you can't fix it it will be nice to add information about it in the readme.

tolitius commented 7 years ago

Could you give a more specific example with ring, app and routes?

check out:

All of the above based on ring. Let me know if this helps.

kwladyka commented 7 years ago
(def app
  (-> handler
      wrap-keyword-params
      wrap-nested-params
      wrap-params
      wrap-restful-format))

Like about situation above.

def is set before (mount/start) so app has value with not started state handler. This value doesn't change after (mount/start). This is to solve by just adding app by defstate but first somebody have to know mount doesn't work with def.

tolitius commented 7 years ago

I am not sure why do you need to use def in this situation, since it will be evaluated every time this namespace is required / evaled in REPL / etc.

Why can't you do something like:

(defroutes app-routes

  (GET "/" [] "welcome to smsio!")

  (POST "/sms/:from/:to/:msg" [from to msg]
    (generate-string
      @(send-sms {:from from
                  :to to
                  :body msg}))))

(defn start-www [{:keys [www]}]
  (-> (routes app-routes)
      (handler/site)
      (run-jetty {:join? false
                  :port (:port www)})))

(defstate web-server :start (start-www config)
                     :stop (.stop web-server))  ;; it's a "org.eclipse.jetty.server.Server" at this point

you can follow the above example app here.

kwladyka commented 7 years ago

I can. As i wrote:

This is to solve by just adding app by defstate but first somebody have to know mount doesn't work with def.

So suggestion of this issue is to fix it for def or write in the readme it doesn't work with def.

tolitius commented 7 years ago

I just don't think

it doesn't work with def

is accurate. here is why:

mount has two modes clj and cljc. Here is more details of why this is the case.

In clj mode, a state is a managed Clojure var. When (mount/start) is called, it changes the value of the state to its "started" value. If you previously had another var linked to it, this other var (i.e. app) would not change, since it is not managed, and this is how Clojure vars work in general. Once you def something it stays the same.

In cljc mode your example would work as you would expect it:

boot.user=> (require '[mount.core :as mount :refer [defstate]])
nil
boot.user=> (mount/in-cljc-mode)
:cljc
boot.user=> (mount/defstate foo :start 42)
#'boot.user/foo
boot.user=> (def app foo)
#'boot.user/app
boot.user=>
boot.user=> (mount/start)
{:started ["#'boot.user/foo"]}
boot.user=>

boot.user=> foo
#object[mount.core.DerefableState 0x1ff8da72 {:status :ready, :val 42}]
boot.user=> app
#object[mount.core.DerefableState 0x1ff8da72 {:status :ready, :val 42}]
boot.user=>

boot.user=> @foo
42
boot.user=> @app
42
kwladyka commented 7 years ago

Thanks to explain it to me. So i can only suggest to add short info about this difference between normal mode and cljs mode in readme. But not in ClojureScript section only. Or maybe only i spent a little time to discover this :)

kwladyka commented 1 year ago

not active