Fork me on GitHub
#om
<
2016-09-20
>
v3ga01:09:19

is om next production ready? i’m getting ready to use it for a toy app but ive been reading about it and watching talks on it for a while

petterik09:09:33

@decim It depends on your definition of production ready 🙂 I think I read somewhere that the creators of #untangled - the framework based on om.next - are running om.next in production, not sure. I expect to go to production "soon". It takes a while to learn all the concepts and you might have to read some source code to get there. Om.next is officially alpha, but to my surprise the API has changed very little over the year it's been out. The internals have changed a lot more, which has been a good thing. Personally, I've been using om.next since alpha-1 and if I had to start over, I'd choose om.next again. The speed of which I can develop new solutions/features while maintaining/removing my old code is insane to me

mitchelkuijpers09:09:41

Is there anyone here who has experience with unions queries? I am trying to use them for a wizard of 3 steps, but I am fighting om.next it seems like

hlolli12:09:58

Is this expected behaviour that (om/component? this) returns false in query but true in render?

anmonteiro12:09:25

@hlolli it will return false in query when it is passed the class

anmonteiro12:09:46

it is a static method for that reason. It can be passed the class or the instance

anmonteiro12:09:08

before first rendering the app, Om Next needs to know the whole app query without instantiating any components

hlolli12:09:57

yes, that's what I thought, makes sense, I guess there's no black magic involved here then 🙂

hlolli13:09:25

I belive, but not sure, that with update to alpha45, that my remote reads changed from being '[(:key {:with "params"})] into '[:key (:key {:with "params"})], not sure tough.

ethangracer13:09:29

@decim @petterik there is one company using Om Next with Untangled in production, I haven’t heard that they have had any issues with it since release

ethangracer13:09:25

we are still in beta but planning to release in the near future, but we currently have it running in our release environment and the framework itself is working just fine

v3ga13:09:03

@ethangracer: cool. I'll give it a try

ethangracer13:09:40

@decim great, feel free to hit us up in #untangled if we can be any help

mitchelkuijpers14:09:23

I am rerooting a query and run into a problem there:

(defmethod read :after-install-page/current-step
  [{:keys [parser query ast target state] :as env} k _]
  (let [st @state
        step (after-install-page-step st)
        query (step query)]
    (when-not (empty? query)
       (if (nil? target)
         (let [res (parser env query)]
           {:value res})
         (let [res (parser env query target)]
           (when-not (empty? res)
             {target (assoc ast :query res)}))))))
I use this combined with a union and somehow it will resolve correctly in the end but when the remote merges the data in it will return only the result of the remote query and at that point my union component will fail to generate a correct ident...

mitchelkuijpers14:09:37

(def step->component
  {:after-install-page.step/choose-space ChooseSpace
   :after-install-page.step/confirmation Confirmation
   :after-install-page.step/synchronizing Synchronizing})

(def step->factory
  (zipmap (keys step->component)
          (map om/factory (vals step->component))))

(defui ^:once StepsUnion
  static om/Ident
  (ident [this {:keys [after-install-page/step] :as p}]
    (.log js/console "gen ident" p)
    [step :step])
  static om/IQuery
  (query [this]
    (zipmap (keys step->component)
            (map om/get-query (vals step->component))))
  Object
  (render [this]
    (let [{:keys [:after-install-page/step] :as props} (om/props this)]
      ((step->factory step) props))))
my union component if that’s even important

hlolli14:09:56

{target (assoc ast :query res)} what does target resolve to? Just curious, intersting code 🙂

mitchelkuijpers14:09:58

target will :remote in my case

jfntn14:09:37

@mitchelkuijpers shouldn’t you return both :value and :remote if there’s a target?

mitchelkuijpers14:09:59

I have tried that but that didn’t seem to help

anmonteiro14:09:48

@jfntn @mitchelkuijpers returning :value and :remote at the same time doesn’t influence anything, as they’re collected at different phases of the parser execution

anmonteiro14:09:09

@mitchelkuijpers I don’t really understand your problem; could be something related to normalization?

anmonteiro14:09:33

probably worth sharing your send function. If that doesn’t clear things up, I suggest putting together a minimal example I can look at

jfntn15:09:36

Is it possible to parametrize a link read like [([:some/key _] {:foo ?foo})]?

anmonteiro15:09:07

@jfntn should be. that said, probably hasn’t been tested a lot so you might run into bugs

anmonteiro15:09:28

if something is not working for you, I’m happy to look at a minimal case and figure why that is

mitchelkuijpers15:09:45

@anmonteiro My send function is kind of dirty 😛 hold on

mitchelkuijpers15:09:14

Ill remove some error handling because that is really noisy

mitchelkuijpers15:09:36

(defn send-remote-request
    [params cb]
    (let [c (promise-chan)
          original-params (:remote params)
          payload (-> (utils/strip-ui (:remote params))
                      remove-fallbacks)
          {:keys [query rewrite] :as t} (om/process-roots payload)
          children (:children (om/query->ast query))
          affected-paths (into #{} (map :key) children)]
      (om/transact! reconciler `[(~'ui/set-loading ~(into {} (map (fn [k] [k {:status :loading}])) affected-paths)) :ui.loading?])
      (.send XhrIo relations-endpoint
             (fn [e]
               (this-as this
                 (->> (case (.getStatus this)
                        200 (let [callback-payload (rewrite (t/read (om/reader) (.getResponseText this)))
                                  reads-payload (into {}
                                                      (filter (comp keyword? first))
                                                      callback-payload)
                                  mutations-payload (into {}
                                                          (filter (comp symbol? first))
                                                          callback-payload)
                                  {:keys [reads mutations]} (collect-after-remote-mutate-reads-and-mutations mutations-payload children)
                                  reads-after (into #{:ui.loading?} reads)
                                  mutations-after (into `[(~'ui/set-loading ~(into {} (map (fn [k] [k {:status :done}])) affected-paths))]
                                                        mutations)]
                              (cb callback-payload)
                              (into mutations-after reads-after))
                        ;; ERROR HANDLING removed for clarity
                        )
                      (om/transact! reconciler)))
               (async/put! c :remote-finished))
             "POST" (t/write (om/writer) query)
             #js {"Content-Type" "application/transit+json"
                  "Authorization" (str "JWT " (utils/token))})
      c))
There is a channel between this but that is not important for now

jfntn15:09:59

@anmonteiro ah yes it does work! That was on me, minimal test cases ftw

(t/deftest parametrized-link-test
  (let [PLink (ui static om/IQuery
                  (query [this] '[([:some/key _] {:foo ?foo})])
                  static om/IQueryParams
                  (params [this] {:foo "bar"}))]
    (t/is (= (om/get-query PLink) '[([:some/key _] {:foo "bar"})]))))

anmonteiro15:09:53

@jfntn happens to me a lot to forget quoting (`’`) the parameterization, which leads to unexpected results

mitchelkuijpers15:09:46

Hmm giving the send function the original query fixes it

anmonteiro15:09:16

@mitchelkuijpers I was just going to write that the problem is probably process-roots

anmonteiro15:09:26

I don’t think it keeps the metadata necessary for normalization

mitchelkuijpers15:09:31

But i remember that my mutations break then

mitchelkuijpers15:09:22

Is there some kind of sane rule on when to give the cb the query and when not?

anmonteiro15:09:05

@mitchelkuijpers there is. you give cb an explicit query if the returned data doesn’t match your root query

anmonteiro15:09:26

e.g. you want to use the query that you pass explicitly for normalization purposes when merging the remote data

anmonteiro15:09:39

otherwise Om Next will normalize against the root query of your application

anmonteiro15:09:41

that’s it!

mitchelkuijpers15:09:40

Hmm but then my send function somehow has to know about particular reads? I think I’ll have to think about this some more. Thank you for the help anyway

mitchelkuijpers15:09:16

is this valid btw? I get this query in the send function:

[{:after-install-page/current-step
  [({:confluence.autocomplete/spaces [:name :key]} {:q ""})]}]

mitchelkuijpers15:09:41

Ow wait that is because of the query param.. duh

peeja15:09:58

What does db->tree do with params? Ignore them?

levitanong17:09:18

@peeja it does seem like db->tree doesn’t do anything with params. AFAIK it works with the query to denormalize some data given the app state.

levitanong17:09:52

@anmonteiro I have a compassus question. Say, the routes are practically unbounded, and you have routes that look like /article/22/view, where 22 is an article ID. Bidi supports this, but based on the documentation, it isn’t clear how one would pass parametrized bidi routes to an om.next class. My first guess is to use wrap-render to pass the matched article ID as a prop down to the factory, if the route is appropriate. Can you confirm this?

levitanong17:09:07

Oh wait, I think I figured it out. Silly me! It’s in set-route! sorry for the noise.

anmonteiro17:09:41

@levitanong yeah, you can use set-route! to add additional information to your app state

anmonteiro17:09:53

note, however, that’s currently unreleased

anmonteiro17:09:03

if you want to try it out locally clone the repo and boot build-jar

levitanong17:09:15

Haha, I noticed just now. I was wondering why the docs say that the arg is only queue?.

peeja17:09:31

@levitanong I mean params in the query (like [(:foo {:some "params"})])

anmonteiro17:09:40

I should start developing in an alternate branch so that those documentation changes don’t reflect too early 🙂

anmonteiro17:09:48

the mixin stuff is also unreleased

anmonteiro17:09:52

e.g. wrap-render

anmonteiro17:09:13

more to come now that we have mixins, though. It’s an awesome extension point for the future

anmonteiro17:09:20

I’ll remove the :history config key too 🙂

anmonteiro17:09:23

it’ll become a mixin.

levitanong17:09:36

@peeja I wouldn’t use db->tree in conjunction with a transact query.

anmonteiro17:09:39

just need to get around to it

peeja17:09:49

Not a mutation, a read

anmonteiro17:09:08

@peeja db->tree will ignore the params

anmonteiro17:09:22

why would you need them?

levitanong17:09:31

Oops! need to have my eyes checked

levitanong17:09:58

re: history and mixins: exciting!

anmonteiro17:09:35

also thinking about adding a macro to construct an application whenever all those keys are removed

anmonteiro17:09:02

in Rum you can specify mixins like defc CompName < mixin1, mixin2

anmonteiro17:09:36

but then again, gotta find the time to do all that

levitanong17:09:12

wait, when which keys are removed?

anmonteiro17:09:43

maybe :reconciler-opts

anmonteiro17:09:26

if there’s only 1 or 2 keys in the configuration map, a macro can cover that easily

anmonteiro17:09:42

just for syntax sugar

levitanong17:09:35

for the record, I’m frightened by option #2 😛

anmonteiro17:09:05

I also don’t like option #1

anmonteiro17:09:48

took me 2 months on-and-off to come up with an elegant solution for this one: https://github.com/compassus/compassus/issues/3

levitanong17:09:49

Sadly, I don’t know enough about compassus to give a helpful alternative 😞

anmonteiro17:09:09

so that one will sit there until I have some bright idea 🙂

levitanong17:09:04

btw, in om.next would you say that the :tempids key in default-merge is for resolving tempids into stable ids?

peeja18:09:54

@anmonteiro Re params: In some cases I need to get the first element of a list; in other cases I need to paginate lists. I didn't actually expect db->tree to do anything useful with params, since it obviously can't know what to do with them, but it occurred to me that I hadn't thought about it before.

peeja18:09:13

When I get there, I guess I'll have to be more sophisticated in my parser

mitchelkuijpers18:09:21

@levitanong yes (thats what i use it for anyway)

peeja18:09:53

I find it a bit frustrating that db->tree does everything you need for reads in most cases, but if you need to alter that behavior slightly you have to reimplement everything from scratch.

anmonteiro18:09:36

@peeja I don’t see how that applies in your case

anmonteiro18:09:05

if you want to return only N elements in a list, pass a N-element list to db->tree

anmonteiro18:09:33

I mean, you can can read the params in the parser

anmonteiro18:09:02

and pass the N-element list to db->tree

peeja18:09:17

I'm talking about something like {[person/by-id 5] [(:person/pets {:index 0})]}

peeja18:09:42

I know the person's id, and I want to get their first pet

peeja18:09:06

I don't get the params in the parser (as params, anyway) unless I recurse in the parser

peeja18:09:19

which is what db->tree is designed to avoid

peeja18:09:22

The db->tree way gives you lots of control at the root query's props and joins, but no control over how deeper keys work. You have to make the parser read recursively for that.

anmonteiro18:09:54

@peeja they are in the AST

peeja18:09:20

I mean, yeah, they're in there. But (defmethod read :person/by-id …) is a weird place to care about the params to :person/pets.

petterik18:09:52

@anmonteiro regarding :history being taken away, thought I'd let you know that I'm using the id being added to history in transact*. Having a transaction id is very important our app

anmonteiro18:09:14

@petterik I was talking about Compassus 🙂

petterik18:09:23

oh! nevermind then 🙂

petterik18:09:14

We're branching off our app-state when doing optimistic updates and rebasing the following transactions on the server response of the optimistic update. We're using the id as the "commit hash" of the stable state before the optimistic update

petterik18:09:31

Will probably write a blog post about this at some point

anmonteiro18:09:21

@petterik that’s cool stuff. one really cool thing I’ve thought about as well but never got around to actually making a POC is that we can also have functions in our applications that resemble datomic.api/with

petterik18:09:13

om/ui-with? 😮

petterik18:09:12

actually, I don't get it. When would you use that?

ethangracer18:09:45

@petterik I think it’d be conceptually closer to om/transact!-with

ethangracer18:09:20

maybe for testing more so than production use?

petterik18:09:36

I sort of do a with in my rebase thingy, to re-apply mutations:

(defn apply-mutations [reconciler state mutations]
  (let [parser (get-parser reconciler)
        env (to-env reconciler)
        _ (parser (assoc env :state state) mutations nil)
        db-after @state]
  db-after)

petterik18:09:19

It'd be useful if we could have something that resembles datomic.api/with in the om.next namespace

anmonteiro18:09:05

@petterik: that's the use case. Evaluating the outcome of txs without actually committing them

petterik18:09:05

I'm using a custom :merge to jump between app-states

peeja19:09:22

I'm a little confused about query ASTs. They include a :query at every node, which means that if you transform the AST, the queries are wrong (or you have to update them). Is there a best practice here?

peeja19:09:32

Who actually uses the :querys?

peeja19:09:26

Unrelated: What's the difference between invariant and assert? Is it just a customized assert?

mitchelkuijpers19:09:19

@peeja I use them but I am a bit confused about them at times.. Right now i have a union where you have to invent a key to nest all of the data under but I actually want to do a normal query under that key so in the read function i do {:remote (assoc ast :query res)} where res is the subquery which I got by calling (parser env query target)

mitchelkuijpers19:09:32

That actually works and it also normalizes remote data that comes in.. the only problem is that the remote query now get’s nested under the union key

peeja20:09:30

Ah, it looks like :query should be treated as a sort of cache, so it's sufficient to dissoc the :query if you change the AST.

peeja20:09:10

Hmm, no, I take that back. That just confuses it. 😕

Joe R. Smith22:09:50

I have a random uuid I want to use in both the :action and :remote (params) in a mutate parser. simply closing over the uuid though doesn’t guarantee that the same id is used in both the action and sent to the remote, e.g.: (let [uuid (random-uuid)] {:action #( … uses uuid …) :remote (assoc-in ast [:params :event/id] uuid)}). I’m assuming it is bc the mutation can be called multiple times but, while the action function has closed over my uuid, the :remote keyval could change each call.

Joe R. Smith22:09:02

Should I be reading any values used in the :remote keyval from function args / state?

anmonteiro22:09:56

@solussd that’s right, the mutate function can be called multiple times. can’t you send the UUID as a param to the mutation?

Joe R. Smith23:09:13

Yeah, I’ll find a way. 🙂 thanks!