mount

2018-11-01T12:08:06.011200Z

👋 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?

2018-11-01T12:09:15.012100Z

the remote resources will be: 1) a big .edn map ; 2) a datascript database

2018-11-01T12:39:57.013600Z

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

2018-11-01T13:25:40.013700Z

arnout 2018-11-01T14:14:59.014800Z

@loganpowell You probably don't want to use core.async here, as you want the :start expression to wait before the result is loaded.

2018-11-01T14:15:42.015300Z

@arnout thank you! How would I async load the resource then?

arnout 2018-11-01T14:16:15.015600Z

Why do you want to load the resource asynchronously?

2018-11-01T14:17:45.017500Z

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

arnout 2018-11-01T14:26:34.017900Z

Yes, I understand. Why has this to be asynchronous?

2018-11-01T14:27:15.018700Z

Oh, I just thought that was the only way to do a GET request (http) ?

2018-11-01T14:27:23.018900Z

is there another way?

arnout 2018-11-01T14:33:04.020200Z

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=.

arnout 2018-11-01T14:33:34.020500Z

To get this to work, you'd get:

(defn somefunctionrequiresconfig []
  (go
    (let [conf (<! @config)]
      ... magic ... ))

arnout 2018-11-01T14:34:45.020800Z

Which is awkward.

arnout 2018-11-01T14:35:31.021500Z

I'm not sure, maybe you want a loop inside the config defstate :start expression, that waits until the resource been loaded.

2018-11-01T14:43:22.023Z

hmm..

2018-11-01T14:43:59.024100Z

so, the issue is that I need @config in a number of functions that aren't defined within a go block

arnout 2018-11-01T14:44:59.026200Z

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 ... )))

2018-11-01T14:46:03.027300Z

So, I'm not wrong that the mount docs don't cover this use case...?

2018-11-01T14:46:20.027600Z

I really thought it seemed like a perfect fit

2018-11-01T14:47:16.028300Z

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

arnout 2018-11-01T14:47:38.028600Z

I guess, it's more suitable for starting things up synchronously.

2018-11-01T14:47:51.029100Z

plumbing the <!'en val through all the underlying children...

2018-11-01T14:48:08.029700Z

until it reaches the nested function that needs it

arnout 2018-11-01T14:48:24.030400Z

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.

2018-11-01T14:48:28.030500Z

Could I use an atom?

2018-11-01T14:49:13.031600Z

let me think about the initialize approach... how/where would I put it in?

2018-11-01T14:49:41.032100Z

The library is basically exported as a single function

2018-11-01T14:49:54.032400Z

it's not really an app

2018-11-01T14:51:47.032900Z

so, maybe I wrap the exported function in a promise, that's what you mean, right?

2018-11-01T14:52:18.033600Z

put the resource in an atom that I can use in the rest of the underlying functions?

arnout 2018-11-01T14:52:45.034200Z

If it's only one function, then yes, perfect. That way you let the App programmer decide how to deal with the inherent asynchronously.

2018-11-01T14:53:09.034800Z

yes, the function has a callback API

2018-11-01T14:53:15.035100Z

that seems like a good idea

arnout 2018-11-01T14:53:16.035200Z

Yes, you could use an atom, such that the consecutive calls are quicker.

2018-11-01T14:53:35.035500Z

hmm... interesting . Let me give that a go

2018-11-01T14:53:53.035900Z

seems simpler than using mount

2018-11-01T14:54:31.036800Z

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?

arnout 2018-11-01T14:54:48.037100Z

Yes, very much so.

2018-11-01T14:54:54.037300Z

ah, ok

2018-11-01T14:55:02.037600Z

well then, on to the Promise land 😄

arnout 2018-11-01T14:55:14.037900Z

Haha, good luck!

2018-11-01T14:55:20.038100Z

Thanks man

tolitius 2018-11-01T15:52:01.042400Z

> 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

arnout 2018-11-01T15:55:01.043Z

Always good to hear from the one and only author of mount! 😉

2018-11-01T15:59:42.045400Z

Thank you for following up @tolitius. I guess I was kinda also looking forward to working with Mount 😉

tolitius 2018-11-01T16:02:16.045800Z

of course, always great to bounce ideas around

2018-11-01T18:26:01.047800Z

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?

tolitius 2018-11-01T19:49:16.049100Z

@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

2018-11-01T19:55:18.049300Z

Thank you!

tolitius 2018-11-01T19:57:38.049500Z

sure