juxt

malcolmsparks 2021-02-23T08:06:25.002600Z

No, but afaik it's something you can do yourself. @dominicm might be able to help more

dharrigan 2021-02-23T08:45:08.003Z

It may be achievable by using profiles perhaps

flowthing 2021-02-23T12:26:53.004200Z

Yeah, good point, it might be possible to reorganize my Aero config to achieve what I'm looking for. I have a feeling that might be a bit clunky, but maybe I'll give it a go.

flowthing 2021-02-23T12:28:53.004400Z

Also, this might be more of a general Clojure question than a Clip-specific question, but: Given a Ring handler like this:

(def app (wrap-middleware my-handler))
To have your changes take effect without having to restart the HTTP server, you can pass the handler var to http-kit like this:
(org.httpkit.server/run-server #'app options)
Now, I can just re-evaluate (def app ,,,) and my changes take effect. However, with Clip, I have something like this in my Aero config:
:handler {:start (app.server.handler/app {:db (clip/ref :db)})}
:http {:start (org.httpkit.server/run-server (clip/ref :handler) {:port 1337})
       :stop (this)}
Now, re-evaluating app.server.handler/app is no longer enough to make my changes live. I have to do a whole-app reset with clojure.tools.namespace.repl. I haven't figured out a way to arrange things such that I can just re-evaluate my handler and have my changes take effect immediately. Any thoughts?

dominicm 2021-02-25T16:18:30.007Z

This is something I want to investigate further, but it's a pitfall shared with integrant and component. I will add that the performance benchmarks I've done indicate there's no significant difference between using the handler directly, and "creating" the handler on every request.

flowthing 2021-02-25T18:00:57.007400Z

Interesting, thanks! With the amount of traffic my apps get, that might indeed be an option. I also tried hacking together a solution with alter-var-root, but I couldn't make it work. I haven't used Component, but Integrant does indeed suffer from the same problem.

flowthing 2021-02-25T18:03:07.007600Z

Maybe there's a way to use profiles to recreate the handler function on every request in dev but not in prod.

dominicm 2021-03-27T10:19:34.006600Z

You could do this:

(defn app [& args]
  (if (:direct-linking *compiler-options*)
    (wrap-middleware (apply my-handler args))
    (fn [req]
      ((wrap-middleware (apply my-handler args)) req))))
And something could potentially automate that for you too

dominicm 2021-03-27T10:20:31.006800Z

Or maybe even (defn app [{:keys [re-eval?] :as opts}] (if re-eval? (fn …) (wrap-middleware …)))

dominicm 2021-03-27T16:20:27.007Z

https://cljdoc.org/d/metosin/reitit/0.5.12/doc/advanced/dev-workflow#an-easy-fix basically this I guess, dev-router and prod-router :)

flowthing 2021-03-27T17:14:15.007300Z

Yeah, that's probably the way to go. I'm always a bit wary of code that works differently in dev and prod, though. 🙂

dominicm 2021-03-28T07:53:08.007500Z

I just hacked together a proof of concept where clip can reconstruct your function every time it's called. It's a little bit funky (it calls your code, does a fn? check, then decides to rewrap it in that case). This might be a good use-case for custom lifecycles (as that's how I hacked it in, it runs after :start and detects the fn and replaces it in that case. It might also be a good use-case for some kind of explicit transformation system.

1
flowthing 2021-04-03T16:04:38.012600Z

Looks very cool! I'll definitely give it a try next week at work.

flowthing 2021-04-07T09:43:58.013Z

Noticed a typo: relodable-fn. Other than that, the reloading part seems to work great. :thumbsup::skin-tone-2: I think this is a hugely useful feature. However, calling juxt.clip.repl/stop fails with "Unable to evaluate form (#'org.httpkit.server/run-server #object[juxt.clip.repl$relodable_fn$reloadable_wrapper__1914 0x33590414 \"juxt.clip.repl$relodable_fn$reloadable_wrapper__1914@33590414\"] {:port 7070})" with that SHA. I'm probably doing something wrong. I don't quite understand what you mean by this: > you'll need to make sure they're not coming from edn as symbols (e.g. by just assoc it on in your code portion). All I did is updated deps.edn to use that SHA and added this into my Aero config: :reloads #profile {:dev {clojure.lang.Fn juxt.clip.repl/relodable-fn}}

dominicm 2021-04-09T09:43:37.013700Z

That error sounds familiar, but I don't remember why 🙂

dominicm 2021-04-09T09:44:17.013900Z

Very odd that it seems to be running your start function on stop

dominicm 2021-04-09T09:44:41.014100Z

> you'll need to make sure they're not coming from edn as symbols (e.g. by just assoc it on in your code portion). ^ this isn't relevant with the later sha I think 🙂

flowthing 2021-04-09T09:52:18.014300Z

Yeah, I'll try to find the time to dive into the problem next week.

dominicm 2021-04-09T10:06:11.014500Z

Ah, I see the problem

dominicm 2021-04-09T10:06:20.014700Z

http-kit returns a function, so clip is trying to reload it for you :)

dominicm 2021-04-09T10:08:12.014900Z

@flowthing This solved it:

(def other-system-config
  {:components
   {:handler `{:start (handler {})}
    :http '{:start (org.httpkit.server/run-server (clip/ref :handler) {:port 8080})
            :stop (this)}}
   :reloads
   `{clojure.lang.Fn repl/relodable-fn
     :http nil}})

flowthing 2021-04-09T10:09:10.015100Z

Ah, of course! Thanks, I’ll try that out.

dominicm 2021-04-09T10:09:15.015300Z

When this hits master, API might actually be more like:

(def other-system-config
  {:components
   {:handler `{:start (handler {})}
    :http '{:start (org.httpkit.server/run-server (clip/ref :handler) {:port 8080})
            :stop (this)
            :reload nil}}
   :reloads
   `{clojure.lang.Fn repl/relodable-fn}})
As I think it's important to localize how the reload works. On the fence about whether matchers should be predicate based or some such.

dominicm 2021-04-09T10:11:17.015500Z

Could definitely see some kind of "convention" where a team does:

(def other-system-config
  {:components
   {:handler/a `{:start (handler {})}
    :handler/b `{:start (handler {})}
    :router '{:start [["/about" (clip/ref :handler/a)]
                      ["/foobar" (clip/ref :handler/b)]]}
    :http '{:start (org.httpkit.server/run-server (clip/ref :handler) {:port 8080})
            :stop (this)
            :reload nil}}
   :reloads
   {(fn [k _v] (= "handler" (namespace k))) repl/relodable-fn}})

dominicm 2021-04-09T10:11:52.015700Z

General solution is good, but API is still open for debate.

dominicm 2021-04-09T10:18:19.015900Z

Could also add some data to the exception when this happens too, and fall back onto the original. Not sure if stop should operate on the original item or the wrapped one either…

2021-02-23T12:57:43.006Z

@flowthing Your configuration looks like good for me. You can check my following repo for which eloaded works: https://github.com/PrestanceDesign/todo-backend-clojure-reitit/tree/clip Maybe it can help.

flowthing 2021-02-23T13:02:04.006100Z

As far as I can tell, in that example, to have changes to your handler or router take effect, you have to call (user/reset), which is what I want to avoid.

2021-02-23T13:33:03.006300Z

OK I will better read your request. 👍