👋 Newb to Mount here. I'm very excited to have heard about this library and I can't believe it's taken me this long to find it. I have a use case wherein I believe it to be a perfect fit. I'm working on publishing an NPM library (using #shadow-cljs ) written in cljs. I have some configs that I'd like to keep out of the library and be able to update independently so users don't have to update their library every time the config gets updated (new data sources). I've been looking through the docs and it seems that "runtime arguments" might be the right fit. Here's my first question: How would I load a remote resource (URL) and store it to be able to deref
it assuming it would be loaded before any function the user uses fires off?
the remote resources will be: 1) a big .edn
map ; 2) a datascript database
My solution thus far has been to pull these resources on every function call that needs them using core.async
, but I'm hoping I can just "call in" these resources once per runtime instead of on every call
@loganpowell You probably don't want to use core.async here, as you want the :start
expression to wait before the result is loaded.
@arnout thank you! How would I async load the resource then?
Why do you want to load the resource asynchronously?
it will be a remote resource/outside the bundle. I'll be publishing an NPM library and want to be able to update a datascript DB and a config file so that the library (functions not changing) doesn't need to be updated to take advantage of the latest config data available
Yes, I understand. Why has this to be asynchronous?
Oh, I just thought that was the only way to do a GET
request (http) ?
is there another way?
Hmm right, maybe not. But in above code, the problem is that a) the config
state might not be ready when you call somefunctionequiresconfig
, and the (go ...
) form returns a channel, not the result in =conf=
.
To get this to work, you'd get:
(defn somefunctionrequiresconfig []
(go
(let [conf (<! @config)]
... magic ... ))
Which is awkward.
I'm not sure, maybe you want a loop inside the config
defstate :start expression, that waits until the resource been loaded.
hmm..
so, the issue is that I need @config
in a number of functions that aren't defined within a go block
On a side note, I tend to advice (in the mount-lite documentation) not to use mount(-lite) in libraries, only in application code.
Maybe your library API could have the function: (initialize)
, which returns a js/Promise (or a core.async channel, if you know for sure that your library consumers are also using ClojureScript). Then users of your library could do:
(.. (yourlib/initialize)
(then #( ... start the rest of my app ... )))
So, I'm not wrong that the mount
docs don't cover this use case...?
I really thought it seemed like a perfect fit
Currently, I've made it work by passing an arg all the way down a parent function that's housed in a go block, but this requires a lot of - hopefully - exorbitant arg passing
I guess, it's more suitable for starting things up synchronously.
plumbing the <!
'en val through all the underlying children...
until it reaches the nested function that needs it
How about above approach? That way you can load your resources asynchronously, bind them globally if you want (in order not to pass it to all of your functions), and have the library consumer be in control.
Could I use an atom
?
let me think about the initialize
approach... how/where would I put it in?
The library is basically exported as a single function
it's not really an app
so, maybe I wrap the exported function in a promise, that's what you mean, right?
put the resource in an atom that I can use in the rest of the underlying functions?
If it's only one function, then yes, perfect. That way you let the App programmer decide how to deal with the inherent asynchronously.
yes, the function has a callback API
that seems like a good idea
Yes, you could use an atom, such that the consecutive calls are quicker.
hmm... interesting . Let me give that a go
seems simpler than using mount
also, while I have you, if loading remote resources is not a common use case for mount, are loading local configs what it's designed for?
Yes, very much so.
ah, ok
well then, on to the Promise land 😄
Haha, good luck!
Thanks man
> if loading remote resources is not a common use case for mount mount and remote resources are somewhat orthogonal, there is definitely a way to load remote resources: i.e. https://github.com/tolitius/hubble/blob/master/src/clj/hubble/env.clj#L23-L30 is loading configs from Consul before starting other states. > I tend to advice (in the mount-lite documentation) not to use mount(-lite) in libraries I fully in agreement with that: https://github.com/tolitius/mount/issues/91#issuecomment-434040529 hence I believe @arnout's suggestion to just use a function is great
Always good to hear from the one and only author of mount! 😉
Thank you for following up @tolitius. I guess I was kinda also looking forward to working with Mount 😉
of course, always great to bounce ideas around
I haven't had a chance to use mount yet, but looking at the README, it looks like mount depends on the require order to choose which thing to initialize first. Does this mean that using something like slamhound
(which cleans up requires and alpha orders them) could potentially mess things up?
@jvtrigueros have not tried slamhound
but the only thing mount would care is the order in which different namespaces are compiled, which slamhound
should not change
Thank you!
sure