Fork me on GitHub
#keechma
<
2019-06-06
>
carkh10:06:03

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 ?

carkh10:06:16

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

carkh11:06:09

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

carkh11:06:47

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

carkh11:06:09

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

carkh11:06:30

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

mihaelkonjevic16:06:54

@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

carkh16:06:21

oh, what shape should this have ?

carkh16:06:48

just like the app-db maybe ?

carkh16:06:55

and merged later i guess

mihaelkonjevic16:06:13

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

carkh16:06:15

allright nice better do it the "approved way"

carkh16:06:42

hum i had another question

carkh16:06:00

in re-frame a subscription can subscribe to other subrscriptions

carkh16:06:24

which might be useful for caching heavier computations

carkh16:06:43

is it possible to do this ?

mihaelkonjevic16:06:59

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

mihaelkonjevic16:06:27

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

mihaelkonjevic16:06:38

Although in keechma it's more explicit

carkh16:06:15

ok good then !

carkh16:06:18

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

carkh16:06:19

sub> works within a context

mihaelkonjevic16:06:28

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

mihaelkonjevic16:06:43

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

carkh16:06:23

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

mihaelkonjevic16:06:35

I would do it like this:

mihaelkonjevic16:06:41

(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})

carkh16:06:33

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

carkh16:06:52

very nice thanks

mihaelkonjevic16:06:56

and keechma is caching subscriptions on the ui layer

mihaelkonjevic16:06:03

so you don’t need to use form-2 components

mihaelkonjevic16:06:10

when calling sub>

carkh16:06:50

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

mihaelkonjevic16:06:43

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

mihaelkonjevic16:06:29

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

carkh16:06:32

wow gimme a sec to process this

carkh16:06:54

interesting

carkh16:06:20

i've got some stuff to show off too !

carkh16:06:59

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

mihaelkonjevic16:06:39

nice, this looks very useful, I could steal it for our projects 😄

carkh16:06:01

that's more a pattern than code,

carkh16:06:29

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

carkh16:06:40

which are very nice for separation of concerns

carkh16:06:14

but that would return a promise

carkh16:06:34

and the loader function has to return a vector

carkh16:06:38

or sequence really

carkh16:06:57

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

mihaelkonjevic16:06:35

yeah, this makes sense

carkh16:06:08

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

mihaelkonjevic16:06:23

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)

carkh16:06:29

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

carkh16:06:55

couldn't use pipelines in this case

mihaelkonjevic16:06:03

yeah - no controller context

mihaelkonjevic16:06:41

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

mihaelkonjevic16:06:03

maybe it would make sense to make a general pipeline lib

carkh16:06:25

the pipeline monad is a good abstraction

carkh16:06:13

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

carkh16:06:24

oh another more pedestrian question

carkh16:06:57

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

carkh16:06:09

so i have this pipeline

carkh16:06:27

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

carkh16:06:40

this pipeline opens the file open dialog box

carkh16:06:00

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

carkh16:06:13

if he presses cancel, that never is fulfilled

carkh16:06:24

trying it

carkh16:06:28

it works well

carkh16:06:46

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

carkh16:06:56

what happens to these pending pipelines ?

mihaelkonjevic16:06:11

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

carkh18:06:10

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

carkh16:06:14

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

mihaelkonjevic16:06:53

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

carkh16:06:15

the pipeline exclusive thing would maybe help with this ?

mihaelkonjevic16:06:06

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

carkh16:06:36

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

mihaelkonjevic16:06:55

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

mihaelkonjevic16:06:08

If it just waits, then it will never happen

mihaelkonjevic16:06:32

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

carkh16:06:56

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

mihaelkonjevic16:06:38

Yeah, I'll think about adding this internally

carkh16:06:01

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

mihaelkonjevic16:06:29

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

carkh16:06:37

haha sorry =)

carkh16:06:06

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

carkh16:06:30

so pretty rare

carkh16:06:52

anyways thanks for your time and good work

carkh16:06:12

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

mihaelkonjevic16:06:31

No problem, have a nice day

👍 4