-
-
Notifications
You must be signed in to change notification settings - Fork 715
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
decouple app-db and re-frame #106
Conversation
Btw. this would allow me to use re-frame as a library in Plastic. My situation is a bit more complicated, because I'm running a web worker with its own re-frame instance. Some jobs dispatched on worker need some continuation on main thread, so I really need to implement my own routing. If anyone is interested, my overlay over re-frame lives here: |
Looks reasonable to me, fwiw. |
I just tested to include re-frame with this PR applied. And still it is not suitable for me to consume it as a library. Another problem are id->fn, key->fn and other state vars littered around. I cannot create an isolated instance of re-frame with its state. I need two instances of re-frame living in a single javascript context. This is for development, in dev mode I run both main and worker thread in main javascript context to ease development and debugging (cljs-devtools do not work for web workers and devtools in general are not UX optimized for multi-threaded development in javascript). My current solution was to copy re-frame sources two times, each under its own namespace. This is how I got around state being hardcoded. But this led to unwanted code duplication. |
I'm not sure this will help with what you are trying to achieve but ... I have for a while now been considering putting all re-frame state into a single deftype and then making all re-frame.core API functions be defined in terms of that type. (deftype re-frame [^:mutable app-db
^:mutable id-fn
^:mutable key-fn
etc .....]
;; re-frame core
(dispatch [this event] ...)
(dispatch-sync [this event] ....)
(register-sub [....] .....)
(subscribe [...] ...)
... rest of the API
) That would then allow us to create different re-frame instances. It would be backwards compatible because the existing Having said this... the idea remains not 100% thought through (I'm not very experienced with deftype) and, given my current workloads, there's no chance of me doing this in the next couple of months (even though it probably isn't that hard). |
Thanks for commenting. I thought about it last night and I decided to rewrite re-frame to fit my needs. I will try to preserve 0.4.1 API in re-frame.core. Here is my current progress (it probably won't compile, just a proof-of-concept rewrite): Plans:
|
This rewrite was motivated by day8#106. In essence re-frame provides a transducer: state, event -> state. This transducer is able to apply algorithmic transformations on series of events and build final state up. This allows complete decoupling of re-frame handlers, subscriptions and middlewares logic from mechanisms how events are queued, processed and how are their effects applied to app-db. This "bare re-frame" is implemented in frame.cljs. For maximal flexibility bare re-frame must be pure and have no special knowledge of app-db, reagent/ratom and core.async. It must be possible to create multiple independent instances of bare re-frame. For convenience scaffold.cljs is re-implementing original re-frame functionality of v0.4.1 using those new bare primitives. This is exposed as public api via core.cljs. App developer can opt-in using default implementation by requiring re-frame.core. In this scenario app-db is reagent's ratom and events are dispatched and processed via core.async channel. Single re-frame instance is created and kept in re-frame.core/app-frame atom. Plus he/she gets access to the original imperative api to manipulate them. The app developer is also free to pick/implement other means of employing bare re-frame by not requiring re-frame directly and build own scaffold around bare re-frame primitives.
This rewrite was motivated by day8#106. In essence re-frame provides a transducer: state, event -> state. This transducer is able to apply algorithmic transformations on series of events and build final state up. This allows complete decoupling of re-frame handlers, subscriptions and middlewares logic from mechanisms how events are queued, processed and how are their effects applied to app-db. This "bare re-frame" is implemented in frame.cljs. For maximal flexibility bare re-frame must be pure and have no special knowledge of app-db, reagent/ratom and core.async. It must be possible to create multiple independent instances of bare re-frame. For convenience scaffold.cljs is re-implementing original re-frame functionality of v0.4.1 using those new bare primitives. This is exposed as public api via core.cljs. App developer can opt-in using default implementation by requiring re-frame.core. In this scenario re-frame provides a single app-db which is reagent's ratom and runs single event loop where events are dispatched and processed via core.async channel. A single re-frame instance is created and kept in re-frame.core/app-frame atom. Plus developer gets access to the original imperative API to manipulate them. App developer is also free to pick/implement other means of employing bare re-frame by not requiring re-frame.core directly and to build own scaffold around bare re-frame primitives.
This rewrite was motivated by day8#106. In essence re-frame provides a transducer: state, event -> state. This transducer is able to apply algorithmic transformations on series of events and build final state up. This allows complete decoupling of re-frame handlers, subscriptions and middlewares logic from mechanisms how events are queued, processed and how are their effects applied to app-db. This "bare re-frame" is implemented in frame.cljs. For maximal flexibility bare re-frame must be pure and have no special knowledge of app-db, reagent/ratom and core.async. It must be possible to create multiple independent instances of bare re-frame. For convenience scaffold.cljs is re-implementing original re-frame functionality of v0.4.1 using those new bare primitives. This is exposed as public api via core.cljs. App developer can opt-in using default implementation by requiring re-frame.core. In this scenario re-frame provides a single app-db which is reagent's ratom and runs single event loop where events are dispatched and processed via core.async channel. A single re-frame instance is created and kept in re-frame.core/app-frame atom. Plus developer gets access to the original imperative API to manipulate them. App developer is also free to pick/implement other means of employing bare re-frame by not requiring re-frame.core directly and to build own scaffold around bare re-frame primitives.
This rewrite was motivated by day8#106. In essence re-frame provides a transducer: state, event -> state. This transducer is able to apply algorithmic transformations on series of events and build final state up. This allows complete decoupling of re-frame handlers, subscriptions and middlewares logic from mechanisms how events are queued, processed and how are their effects applied to app-db. This "bare re-frame" is implemented in frame.cljs. For maximal flexibility bare re-frame must be pure and have no special knowledge of app-db, reagent/ratom and core.async. It must be possible to create multiple independent instances of bare re-frame. For convenience scaffold.cljs is re-implementing original re-frame functionality of v0.4.1 using those new bare primitives. This is exposed as public api via core.cljs. App developer can opt-in using default implementation by requiring re-frame.core. In this scenario re-frame provides a single app-db which is reagent's ratom and runs single event loop where events are dispatched and processed via core.async channel. A single re-frame instance is created and kept in re-frame.core/app-frame atom. Plus developer gets access to the original imperative API to manipulate them. App developer is also free to pick/implement other means of employing bare re-frame by not requiring re-frame.core directly and to build own scaffold around bare re-frame primitives.
closing, let's move to #107 |
preparations for day8/re-frame#106
This is just a proposal PR. I wasn't able to run tests (OS X) or update examples. I can put more work into this if the general idea is approved.
In Plastic I had to vendor re-frame, because I wanted to use my own app-db and router. Unfortunately
router.cljs
,handlers.cljs
andsubs.cljs
are requiring re-frame'sdb.cljs
directly which didn't give me a chance to cherry-pick them independently.This PR removes direct dependency of
router.cljs
,handlers.cljs
andsubs.cljs
ondb.cljs
. To preserve backward compatibility this dependency is moved tocore.cljs
which should be used as public api by majority of re-frame consumers anyways.The second change is making automatic start of router loop optional. Again, it was moved to
core.cljs
. Library consumers who will not includecore.cljs
can still includerouter.cljs
and kick-start router loop by calling(router-loop some-app-db)
manually (or provide their own).This PR does not touch
undo.cljs
because it is not exposed viacore.cljs
, I'm afraid decoupling it from app-db would break current API. Anyways, I don't need re-frame's undo at this point