keechma 2019-06-06

Dev workflow question : If i'm debugging a "state complex" thing like a wizard-like group of pages/panels, the state will typically be under the entity-db and kv keys. But that state is wiped out on each start-app!... So now, each time i save a file, i need to click again in the browser to rebuild the state. Is something wrong with my setup ? I don't think it would be great to have the whole state detail encoded in the url ?

when only using the on-js-reload trick the changes to my components do not show until a hard refresh in the browser

Guess this'll work, i can refresh when state is messed up

i generally had to change most of defs from examples to defn to have a good reload behavior and fast iterations

actually saving the whole app-db thing will mess with the internals, so just save/restore the kv and entity-db parts

doesn't need to be perfect, just easier during dev

@carkh you can pass the :initial-data attribute to app def when you're starting the app. This will be stored in the app-db atom when the app is started

oh, what shape should this have ?

just like the app-db maybe ?

and merged later i guess

I would do (select-keys @prev-app-db [:kv :entitydb])

allright nice better do it the "approved way"

hum i had another question

in re-frame a subscription can subscribe to other subrscriptions

which might be useful for caching heavier computations

is it possible to do this ?

You can do it in keechma too, the underlying system is the same

Both keechma and re-frame use reagent's reaction macro

Although in keechma it's more explicit

ok good then !

but what woudl be the correct incantation to create a subscription that would go inside that get-kv example

sub> works within a context

when you create subscriptions you will always get app-db as the first arg

and then anything else you passed to sub> as subsequent args

ok so the dependencies would come from where i use it....like in a component (let [my-sub (sub> :my-sub previous-sub)] ...

I would do it like this:

(defn foo-sub [app-db-atom]
  (reaction
   (get-in @app-db-atom [:kv :foo])))

(defn bar-sub [app-db-atom]
  (reaction
   {:foo @(foo-sub app-db-atom)
    :bar (get-in @app-db-atom [:kv :bar])}))

(def subscriptions
  {:foo foo-sub
   :bar bar-sub})

ok so if bar depends on foo, i can do {:bar (massage-foo @(foo-sub app-db-atom))}

very nice thanks

and keechma is caching subscriptions on the ui layer

so you donโ€™t need to use form-2 components

when calling sub>

ok that answers all my questions for today =) thanks again !

btw, just something interesting, not completely related - since keechma is not using any globals, you can cut-off app db on the component level and create new one that you manually update. We used this to animate page level transitions on mobile, where you donโ€™t want to change previous page when the next page is being animated in

(defn renderers->components [components]
  (reduce-kv (fn [acc k v]
               (let [c-meta  (meta v)
                     context (get c-meta :keechma.ui-component/context)]
                 (assoc acc k 
                        (assoc context :components (renderers->components (:components context))))))
             {} (or components {})))

(defn make-internal-ctx [ctx]
  (reduce-kv
   (fn [acc k v]
     (let [c-meta         (meta v)
           renderer       (get c-meta :keechma.ui-component/renderer)
           context        (get c-meta :keechma.ui-component/context)
           components     (or (:components context) {})
           component-deps (vec (or (keys (:components context)) []))
           comp-ctx       (merge context
                                 {:app-db         (:app-db acc)
                                  :components     (renderers->components components)
                                  :component-deps component-deps})]
       (assoc-in acc [:components k] (ui/component->renderer acc comp-ctx))))
   ctx (:components ctx)))

(defn replace-app-db-in-ctx [ctx app-db]
  (let [current-route-fn (fn [] (reaction (:route @app-db)))]
    (-> ctx
        (assoc :app-db app-db)
        (assoc :current-route-fn current-route-fn))))

(defn render-panel [ctx page]
  (let [app-db              (:app-db ctx)
        rendering-page-atom (atom page)
        internal-app-db     (r/atom @app-db)
        internal-ctx        (make-internal-ctx (replace-app-db-in-ctx ctx internal-app-db))
        watch-id            (gensym :transition-watch)]
    (add-watch app-db watch-id
               (fn [key ref _ new-val]
                 (let [current-page   (:key (get-in new-val [:route :data]))
                       rendering-page @rendering-page-atom]
                   (when (or (nil? rendering-page)
                             (= current-page rendering-page))
                     (reset! internal-app-db new-val)))))

    (r/create-class
     {:reagent-render         (fn [_ page]
                                (reset! rendering-page-atom page)
                                (when page
                                  [(ui/component internal-ctx page)]))
      :component-will-unmount (fn []
(remove-watch app-db watch-id))})))

this basically rewrites the deps (both the components and subscriptions), so they all use new app-db

wow gimme a sec to process this

i've got some stuff to show off too !

i think this middleware-loader can do everything in any context

nice, this looks very useful, I could steal it for our projects ๐Ÿ˜„

that's more a pattern than code,

i was trying to use the sieppari library to make use of interceptors

which are very nice for separation of concerns

but that would return a promise

and the loader function has to return a vector

or sequence really

anyways you could just replace that eql/middleware by a graphql/middleware and there you go protocol change in a single line of code

yeah, this makes sense

the trouble with the middleware pattern is that everything needs to be synchronous

yeah, which is why they are not really used in Keechma, although I would say that pipelines can act in a similar fashion (as a series of steps that get executed)

i don't know if that woudl make sense to accept promise return values from the loader functions

couldn't use pipelines in this case

yeah - no controller context

although I did experiment with pipelines in server-side context - where I had more general implementation

maybe it would make sense to make a general pipeline lib

the pipeline monad is a good abstraction

i don't know that it would make sense to abstract more, then you're back at general monadic stuff and there are libraries for that already

oh another more pedestrian question

in my app i open a file, so this opens the file dialog, and html only will warn me when the user press ok

so i have this pipeline

that's called inside a pipeline controller each time the user press a button

this pipeline opens the file open dialog box

and waits for a promise that will only be fulfilled when the user press ok

if he presses cancel, that never is fulfilled

so the pipeline is re-entered each time the user presses the button

what happens to these pending pipelines ?

They sit there waiting, alone :). I guess that since they are blocked in go-loop, the state stays in memory which could mean it's a slow memory leak

๐Ÿ‘

I think you should not do anythign about this promise thing. If i return a promise, the contract is that I will either resolve or reject it. If I break that contract, that's a "me" problem, not a keechma problem

(not that this matters much, unless the user presses that button like a mad man)

If the controller is stopped, I think they should be cleaned up

the pipeline exclusive thing would maybe help with this ?

Hm, this is an interesting one, since the promise is not resolved, it will actually never come to the release step

ohwell not a big deal for me, just a curiosity thing

Exclusive will mark the pipeline as done, but pipeline itself must get to the point where it checks if it should proceed

If it just waits, then it will never happen

Yeah, it's probably not a huge deal, but an interesting problem

alt waiting on a stop channel is the usual thing i guess

Yeah, I'll think about adding this internally

not a problem to me, and most likely not a problem to anyone =)

But now I'm aware of the problem so it's a problem for me :)

that's a degenerate problem due to a specification bug in html5

so pretty rare

anyways thanks for your time and good work

women are clamoring for food here ... gotta go feed them

No problem, have a nice day

๐Ÿ‘ 1