mount

lwhorton 2016-05-20T16:05:43.000247Z

Heya guys .. i’m curious how people manage the ordering dependencies when using mount. For example, if I want to setup a websocket connection on mount/start, but other people need access to that connection when it becomes established, what’s a good way to handle this?

lwhorton 2016-05-20T16:08:07.000248Z

I cannot simply do defstate conn :start do-connection and expect something in another namespace to be able to conn (:register #(… )) for example. Given the way namespaces are loaded, I cannot have a ‘top level’ / ‘raw’ function that’s invoked immediately and expect conn to be available.

lwhorton 2016-05-20T16:08:38.000249Z

Is there a way to hook into a “the :start of this state is complete” -> now do something with state?

zane 2016-05-20T16:09:35.000250Z

Not without defining another defstate.

zane 2016-05-20T16:09:48.000251Z

My impression is that you're basically just not supposed to do that.

lwhorton 2016-05-20T16:10:08.000252Z

I figured that might be the case.. i will have to come up with some other mechanism I suppose

zane 2016-05-20T16:10:21.000253Z

I guess my question would be: Why are you trying to do that?

zane 2016-05-20T16:10:46.000254Z

Having things other than definitions at the "top level" is generally discouraged.

lwhorton 2016-05-20T16:11:47.000255Z

Imagine I only want one ws connection from my client -> server.. but many features might have to subscribe to various channels on that connection’s session. I don’t want a giant list of “when the connection is made hook up all these features”, but instead want the features to somehow be notified of the connection.

lwhorton 2016-05-20T16:12:05.000256Z

The more I type the more I realize this has nothing to do with mount, and I was just hoping mount might make the solution easier than it really is.

zane 2016-05-20T16:13:09.000257Z

I'm not understanding why the features that use the connection wouldn't either be functions or other defstates.

lwhorton 2016-05-20T16:15:07.000258Z

They would, but figuring out a decoupled way to “hand the session over" to them and guarantee bootstrapping order, etc. is the real problem.

lwhorton 2016-05-20T16:15:19.000259Z

But that’s not anything to do with mount, as I mentioned

zane 2016-05-20T16:15:31.000260Z

It kind of does.

zane 2016-05-20T16:16:12.000261Z

If it were me I would have those features take the connection as an argument, and have some defstate call them with it as necessary.

zane 2016-05-20T16:16:18.000262Z

Hope that's helpful.

lwhorton 2016-05-20T16:16:37.000263Z

Ill think on it, thanks

lwhorton 2016-05-20T16:46:34.000264Z

@zane: I think I understand now. I can still guarantee ordering by making sure the feature that wants to use the connection also registers with mount.

(defstate core :start #(ws-namespace/ws-conn-state register-myself))
Where ws-conn-state might return an api as opposed to data.

lwhorton 2016-05-20T16:47:35.000265Z

For each feature that registers I maintain a seq of registerees, and upon a successful connection, let each one know.

zane 2016-05-20T16:49:14.000266Z

Sure. That's one way to do it.

zane 2016-05-20T16:49:56.000267Z

Or you could do it the other way around where the features are defstates and refer directly to the defstate with the connection in it.

lwhorton 2016-05-20T16:50:48.000268Z

I could, but that seems more coupling to me. I would prefer N unknown features requiring a connection, unless I’m misunderstanding you.

lwhorton 2016-05-20T17:03:22.000269Z

Hmm, doesnt seem like defstates are meant to ever be changed beyond :start? I can’t swap! into one of them.

dm3 2016-05-20T17:51:40.000270Z

@lwhorton: can you have many conns in the application?

dm3 2016-05-20T17:54:51.000271Z

with mount it's expected that you declare your dependencies via requires, what zane proposed:

(ns consumer
  (:require [ws-connection :as ws]))
(defstate consumer :start (create-state ws/conn))
(defn do-something [] (... consumer ...))
if you have just one connection - this is not more coupled than having the consumer accept the connection as a parameter IMO

lwhorton 2016-05-20T18:05:41.000272Z

That’s how I decided to run with it ^

tolitius 2016-05-20T18:21:03.000273Z

@lwhorton: I was thinking about it.. https://github.com/tolitius/mount/issues/16 the simple solution has not come to me yet, but it will 🙂 for now, you can definitely do what @zane and @dm3 suggest: i.e. establish implicit dependencies via :require another way you can think of approaching it would be running:

(mount/start #'ws/conn)
(mount/start)
which would start the connection before starting anything else, and then would bootstrap the app

lwhorton 2016-05-20T18:21:53.000275Z

oh that’s a neat idea

lwhorton 2016-05-20T18:22:08.000276Z

I dont mind ‘implicit dependencies’ as you say because, well, i dont think they’re that implicit

lwhorton 2016-05-20T18:22:31.000277Z

imo its no different than any other IoC I might write that does something like [:inject foo bar]

lwhorton 2016-05-20T18:22:57.000278Z

i guess you don’t get the luxury of other neat-o things like startable and other runtime-pre-start configuration options

tolitius 2016-05-20T18:25:08.000279Z

what do you mean by startable?

lwhorton 2016-05-20T18:26:27.000280Z

its a common pattern in most inversion of control containers — my favorite being https://github.com/castleproject/Windsor/blob/master/docs/README.md

lwhorton 2016-05-20T18:27:11.000282Z

it’s just a means to allow dynamic at-runtime configuration.. you can declare things like mixins, overrides, startable/stoppable, decorators, etc.

lwhorton 2016-05-20T18:27:32.000283Z

I haven’t found something similar in clojureland , but I also haven’t had the need (yet)

zane 2016-05-20T18:29:31.000284Z

I'd be interested to hear whether you wind up needing them.

zane 2016-05-20T18:29:59.000285Z

My suspicion is Clojure's philosophy on mutable state will obviate the need for most.

lwhorton 2016-05-20T18:30:59.000286Z

more than likely, but when you start talking about client-side guis where there is a lot of potential for reuse I found things like “on the fly” decorators to be amazing

tolitius 2016-05-20T18:38:53.000287Z

dynamic at-runtime configuration, in your use case (with ws conn), can you share how you would solve it with a startable?

lwhorton 2016-05-20T18:41:05.000289Z

sure, a fun lib I use in jsland https://github.com/mnichols/ankh provides an async startable implementation-

lwhorton 2016-05-20T18:44:23.000293Z

in this sense, the ioc worries about making sure everything is started and “ready to go” and is pretty orthogonal to your application code

lwhorton 2016-05-20T18:51:48.000299Z

@tolitius: ^ (and sorry about the javascript)

dm3 2016-05-20T21:09:45.000301Z

@lwhorton: what bothers you if the above is expressed as

; websocket.clj
(ns websocket)
(defstate socket :start ...)

; impl.clj
(ns impl (:require [websocket :as ws]))
(defstate impl :start (gogo ws/socket))
? Just trying to understand the motivation behind the "injection" part