Fork me on GitHub
#fulcro
<
2018-10-22
>
tony.kay03:10:14

So, I’m fixing incubator’s pmutate! @currentoor and @wilkerlucio, and I realize that the error-marker should really be something that can be seen during loading as well…so I’m going to rename it. I normally would not make such a change to a released API, but since it was just released yesterday I think it’ll be ok.

tony.kay03:10:36

0.0.4 is on clojars already and works, but 0.0.5 is coming in a few minutes, and will rename error-marker.

tony.kay03:10:45

docs will also be expanded to say this

tony.kay03:10:31

Just release 0.0.5 of Incubator to clojars. The improved pmutate! is now working correctly with hard errors, is better documented, and has proper way to distinguish interaction for components that share idents.

🎉 8
tony.kay03:10:05

weird…the asciidoc readme is not refreshing

tony.kay03:10:32

in fact, the entire repo is stale…wonder if github is having problem

tony.kay03:10:27

I think this version is pretty darn good, and includes clear targeting and merging support for the mutation response (the older versions sort of did as well, but it was implemented by side-effecting in the remote and was unclear how to use correctly). The new technique works more like load’s targeting/merging, and doesn’t violate Fulcro’s rules of mutation implementation.

tony.kay03:10:47

yeah…github is having issues…so, if the README you’re seeing says anything about error-marker, then github hasn’t updated…my repo says the commits are pushed, and now github reload of the page is crashing.

tony.kay03:10:14

Updated 25 minutes ago: We continue working to repair a data storage system for . You may see inconsistent results during this process.
Check our status site and @githubstatus on Twitter for updates.

tony.kay03:10:20

ok, I guess I’ll check back tomorrow 🙂

tony.kay04:10:20

The released code uses ::pm/key instead, which is now visible in all phases…and ::pm/returning and ::pm/target now go in pmutate! params. They are a component class and df/load targets for the mutation response (if you want that response merged and targeted).

currentoor04:10:59

Nice I’ll try it tomorrow 😃

hmaurer14:10:18

http://book.fulcrologic.com/#Unions If I have a tree of data (say, an AST representing some language) and nodes don’t have any notion of an ID (they’re just nodes in the tree of a given “type” (i.e. they have have different sets of facts attached to them). What should the indent be?

hmaurer14:10:08

Here’s an example of such a tree:

#:elogic.core.binary-compound{:connective :implication,
                              :left-operand #:elogic.core.predicate{:name "P", :arguments nil},
                              :right-operand #:elogic.core.unary-compound{:connective :negation,
                                                                          :operand #:elogic.core.predicate{:name "Q",
                                                                                                           :arguments nil}}}

hmaurer14:10:01

I would like to build a set of components to render this tree, essentially one component per node “type”, with a query pulling the relevant facts for a specific node

hmaurer14:10:51

basically, my question is: can I use unions without the child components having an ident?

hmaurer14:10:11

(to be clear: the reason why I can’t see components rendering individual nodes having an ident is because those nodes don’t really have an “identity”, and as far as I understand Fulcro’s idea of an “ident” is to assign an identity to a part of the data tree and denormalise it. Here the “identity” of these nodes is just the place where they appear in the tree; the “same node” never appears in two places in the tree)

tony.kay14:10:25

So, you don’t have to normalize, and if you never “mutate” the node, there is no reason to

tony.kay14:10:36

otherwise, give them UUIDs

tony.kay14:10:02

the nodes will have to have unique identity to use union queries, though, because union queries work off of idents

tony.kay14:10:31

but if you have a tree and just want to render it as a tree, the query will be really light, and unions aren’t really necessary…just “switch” off of the node type

tony.kay14:10:38

in a case statement or something

hmaurer14:10:19

@tony.kay correct me if I am wrong but I don’t have access to the props in the query though, right? (the full query is built before the data flows in), so how can I switch the query based on some “type” depending on the props?

hmaurer14:10:52

I can switch the rendering based on the node “type”, but the query will have to pull all the data for every node “type” then

hmaurer14:10:04

unless I’m missing something

hmaurer14:10:10

> otherwise, give them UUIDs I thought about this but then (I think?) Fulcro would normalise my whole expression tree. I’ll have a lot of such expressions, so this would be wasteful performance-wise and useless (since nodes are never referenced in different places, I never mutate them and don’t have a notion of identity, it makes little sense)

tony.kay14:10:20

render them as a tree

tony.kay14:10:33

they are a tree, render them as such…you don’t need queries on components

hmaurer14:10:40

To summarise: the part I am still unclear about after your answer is what the query should look like if I just wish to render it as a tree, without ident on the nodes. I won’t be able to specify on a per-component basis the properties I want to pull out, right? (i.e. :elogic.core.predicate/name when rendering a predicate, etc)

tony.kay14:10:03

The query on some component could just be [:ast] (no join)

tony.kay14:10:07

and the entire tree could be there

tony.kay14:10:18

a prop is an opaque data value of any type

hmaurer14:10:25

right, yeah, that’s the solution I was thinking of. I was hoping I could use queries somehow

hmaurer14:10:31

I’ll do that then

hmaurer14:10:07

thanks 🙂

hmaurer14:10:30

@tony.kay completely unrelated question but may I ask what inspired you when you developed Fulcro? I have never used Om but from what I gather you definitely took inspiration from there. Are there any other projects (perhaps beyond clojure) that guided your design?

tony.kay16:10:43

@hmaurer There’s quite a bit I’ve written and talked about on the website and other places. I did a Defn podcast, which is a good resource, and the website has some history.

tony.kay16:10:34

It really is an extension of the work David Nolen started on Om Next, and quite a bit of Om Next code is actually part of the code base.

tony.kay16:10:23

A lot was going on then: Elm, Om Next, other “pure” systems…I wanted FP, and a good answer for the problems that plague full-stack apps. But a lot of the decisions I’ve made in Fulcro itself had more to do with an aim for good developer experience. I’ve seen way too many projects “hit the wall” when the constructs of their libraries and tools cause them to be unable to cope with their code anymore. Some of that you jsut can’t fix (developers do need to have decent chops and some dedication to not making a mess), but certain ways of doing things that some libraries encourage just don’t scale…so I’ve always had an eye towards solutions that can scale to larger programs, and over time.

tony.kay16:10:21

https://github.com/fulcrologic/fulcro-incubator I just pushed 0.0.6, with a small bump that lets you use declared mutations (declare-mutation) with pmutate! without the need for quoting.

currentoor17:10:34

pmutate! is awesome but one thing that concerns me is composing mutations together, for example i have this

(prim/ptransact!
 this
 `[(a.mutations/start-loading)
   (a.mutations/upsert-vehicle
    ~{:wash-id id
      :img-src (:img-src unconfirmed-vehicle)
      :make    (:make unconfirmed-vehicle)
      :plate   (:plate unconfirmed-vehicle)})
   (cc-terminal/sale
    ~{:amount  (::package/price package)
      :wash-id id})
   (a.mutations/start-loading)
   (a.m.mutations/handle-payment-response
    ~{:wash-id id})
   (a.mutations/finish-loading)])

tony.kay17:10:00

hm…so, yeah

currentoor17:10:15

maybe something like this could work?

(pmutate! this {:pre   `[(a.mutations/start-loading)
                         (a.mutations/upsert-vehicle
                          ~{:wash-id id
                            :img-src (:img-src unconfirmed-vehicle)
                            :make    (:make unconfirmed-vehicle)
                            :plate   (:plate unconfirmed-vehicle)})
                         (cc-terminal/sale
                          ~{:amount  (::package/price package)
                            :wash-id id})
                         (a.mutations/start-loading)]
                :ok    `[(a.m.mutations/handle-ok-payment
                          ~{:wash-id id})
                         (a.mutations/finish-loading)] 
                :error `[(a.m.mutations/handle-payment-error
                          ~{:wash-id id})
                         (a.mutations/finish-loading)]})

currentoor17:10:34

but that’s pretty verbose too

tony.kay17:10:35

mmmm, no, don’t like that

tony.kay17:10:41

so, start-loading does what?

currentoor17:10:56

yeah, that can easily be combined

tony.kay17:10:00

doesn’t need pessimism, so logic goes in action

tony.kay17:10:11

upsert is the “check for errors part”

tony.kay17:10:35

then you want to do the “next sequence”…

currentoor17:10:42

yeah upsert and sale are two distinct mutations that should probably stay separate

tony.kay17:10:43

but only on ok

currentoor17:10:03

so make two calls to pmutate!?

tony.kay17:10:11

and the errors are really different…one is a network/db error (rare), the other is CC, whcih is “common”

tony.kay17:10:25

well, you have a hard dependency on success from one to the next

tony.kay17:10:42

so you’d have to put the pmutate! within the “ok” action of the first

tony.kay17:10:33

but then again, why split it out so much? You can make helpers on the server to do each task, but why not make a single server-interaction mutation for the combo as an abstract wrapper for all of the server things that have to succeed?

currentoor17:10:49

that is a good point

tony.kay17:10:50

oh…the cc-terminal is possibly local to the web iface?

tony.kay17:10:00

not the same “server” interaction

currentoor17:10:23

possibly, but in this case no, both happen on the same server

tony.kay17:10:26

yeah, gotta love async js world 🙂

currentoor17:10:42

lol this is why i prefer JVM to node

tony.kay17:10:33

So, pmutate! is using ptransact underneath…so it is safe to put calls of it in the ok-action and error-action areas

tony.kay17:10:08

sometimes async just pushes you to a “chain” pattern

tony.kay17:10:49

The point of pmutate! is that is associates the progress and result data with a component in the UI

tony.kay17:10:01

to minimize boilerplate everywhere….composition is possible

tony.kay17:10:20

but it would force the intermediate results all to the same component UI

tony.kay17:10:34

that is probably OK…just have not written the code for it…and it will get hairy

tony.kay17:10:07

actually, now that I think of it…I probably won’t. Mainly because there is branching inherent in the problem

tony.kay17:10:31

as soon as you need a chain, there is a branch, whcih needs logic (at least if), and that is not going into the tx notation 🙂

tony.kay17:10:40

so, chaining it is, I think

tony.kay17:10:26

The best you can do in async land where each step possibly has an error is to code it sort of like js-land (callbacks for each branch of possible outcome), and possibly combine as much into one call as possible to minimize the length of the chain.

currentoor17:10:01

yeah i see…

currentoor17:10:05

so right now i didn’t even bother having error handling logic for the upsert-vehicle, i just assumed that would always work

tony.kay17:10:18

net could be down

tony.kay17:10:30

server outage

currentoor17:10:33

shouldn’t auto-retry take care of that?

tony.kay17:10:34

rare, but possible

tony.kay17:10:43

if you’re using websockets with it, then yes

tony.kay17:10:58

but db down and server up will be an exception

tony.kay17:10:02

and auto-retry won’t do that

tony.kay17:10:34

auto retry stops trying if the server communication is ok, but the server refuses an action

currentoor17:10:26

this is probably a bad idea, but might be clean to have a conciser notation for chaining mutations via nested transact! calls

currentoor17:10:06

or nested pmutate! calls

wilkerlucio17:10:08

@currentoor the motivation for me to create pmutate! was because here pretty much every mutation is sensitive and needs to check if it worked fine, specially in an environment were things get deployed all the time and services might have bugs, so we can't "hope for the best", if somethign bad happens the user needs to know

wilkerlucio17:10:37

so to avoid all this boilerplate all around the code, this function encapsulates de process so its easy to have this process everywhere

currentoor17:10:03

@wilkerlucio makes sense, but how do you compose logic? just make aggregate mutations?

wilkerlucio17:10:50

what you mean by compose logic? most of times what we need is to send some info to the server and sometimes update the local db with some response, that's not a lot to it

tony.kay17:10:30

(defmutation composite [params]
  (ok-action [env]  (pmutate! ...))
  ...)

wilkerlucio17:10:32

and you can always have a single mutation that does everything you need in the DB

wilkerlucio17:10:53

this mutation can re-use simple functions if you need to repeat things around

currentoor17:10:57

yeah that’s what i meant

wilkerlucio17:10:23

its better to have a single mutation doing a lot of things in a single atomic change, than trying to compose using multiple mutations, that's been true IME

tony.kay17:10:47

agreed, but in some cases, for example a sequence that requires talking to two remotes, you are forced to compose at this level

currentoor17:10:19

yeah you’re right, it’s just tempting to build abstractions at the mutation level, since transact! let’s combine them and it’s easier to name them separately

tony.kay17:10:23

and I have not tried it, but :component should be in env, so you can pull it for the next call (`pmutate!`’s first arg is this, a component)

8
currentoor17:10:50

also makes things a little more readable IMO

wilkerlucio17:10:03

I can understand the feeling of using multiple mutations, and also its requirement when you need to chain multiple things, but in practice I've not feel the need for it

currentoor17:10:08

though i agree, it’s better not to

tony.kay17:10:36

But transact! is assuming you’re going to compose one thing that has optimistic updates, goes to one server, and “succeeds by default”, with you adding in the logic to deal with everything.

tony.kay17:10:56

but allows you to hit multiple servers

wilkerlucio17:10:57

if you are using a single source of truth like Pathom to wrap everything else, most of the "dependency chain" can be handled at API level, so your UI doesn't have to do it, surely that will be exceptions, but I find then very rare (personally I never had one to be honest)

tony.kay17:10:42

agreed…most interactions, in my experience are pretty straightforward, with a decent amount of boilerplate…these cases of composition across multiple endpoints with no interstitial user interaction are (fortunately) rare

currentoor17:10:26

cool, thanks guys

pserrano19:10:02

(hello-clojurians " : ) ")

pserrano19:10:29

Does anyone here uses material-ui?

devo19:10:30

I do, but not in the context of fulcro. Could probably provide some guesses if you are having any issues though.

pserrano14:10:50

Hi! Thanks for the reply! I already solved my yesterday's problem, but I have no idea of how implement this style:

title: {
    display: 'none',
    [theme.breakpoints.up('sm')]: {
      display: 'block',
    }
How to call theme.breakpoints ? Any idea?

devo15:10:25

(clj->js {
        :display "none"
        (-> theme .-breakpoints (.up "sm")) { :display "block" }
})
for interop with jss

devo15:10:37

fulcro-css should work with components using jss, since both just create classes to be used with components.

devo15:10:59

so instead of creating a jss stylesheet and pulling the classname off of classes from props, you should be able to go through the fulcro-css flow for all components that require colocated css and destructure the classes from defsc.