Fork me on GitHub
#untangled
<
2016-10-13
>
tony.kay00:10:49

0.6.0-SNAPSHOT is on clojars

tony.kay00:10:31

I added a new implementation of load and load-action and am planning to deprecate load-data and load-data-action. The new API is cleaner, and includes a :target parameter that can be used to avoid many post-mutations.

tony.kay00:10:51

The target is also where the load marker will go

tony.kay00:10:56

I'd be interested in hearing feedback. It does make a join for you, so perhaps that isn't ideal...I consider it experimental for the moment, but I'm playing with it to see if it makes sense. It is likely I'll evolve it a bit more...not sure about the required keyword yet, but I think it makes sense for "global loads"

jasonjckn00:10:36

@tony.kay i definitely think that's a step in the right direction

jasonjckn01:10:28

@tony.kay one of the patterns that I would like to see emerge in our code-base is that every table we have in our DB, there's a corresponding table in our om.next normalized DB, so there's a 1:1 mapping between both DBs

jasonjckn01:10:04

@tony.kay So apart of better communicating on my end, the pattern means loading data into idents is very common, as well as queries that join on idents

tony.kay01:10:31

@jasonjckn Yeah, that latter use-case is one I’m still pondering

tony.kay01:10:13

I could make load take a keyword or ident instead of just a keyword. But then :target does not make sense. Maybe a third function load-entity

tony.kay01:10:29

we originally had load-collection and load-singleton

tony.kay01:10:38

the new load serves well for collections and singletons where the singleton isn’t necessarily in a table. That sounds like a step backwards at first, but I think it might just be a good refinement. I don’t like swiss-army-knife functions

tony.kay02:10:47

I jus pushed an update to 0.6.0-SNAPSHOT. load now accepts either a keyword or explicit ident, which will allow you to load an entity into a table directly. It still needs the component query for normalization, so it really wasn’t much of a big deal.

tony.kay02:10:43

I also fixed all open issues that had reproducible cases, and closed the ones that seemed more like noise.

gardnervickers17:10:09

@tony.kay That should be very helpful to us. I’m playing around with df/load right now, but having trouble getting it to work in places I was previously using load-data. From the docs, I would assume these two calls would result in similar behavior. load-data correctly loads the returned idents under [:profile-list/by-id :all], while load sets [:profile-list/by-id :all] to nil.

(df/load-data reconciler
     (om/get-query profile/AllProfiles)
      :ident [:profile-list/by-id :all])
(df/load reconciler
    [:profile-list/by-id :all]
    profile/AllProfiles
    {})
Should we handle setting [:profile-list/by-id :all] to the collection of returned idents in a post mutation now?

tony.kay17:10:06

no, that should work

tony.kay17:10:26

I did the ident thing last night before bed. I thought I tested it though

tony.kay17:10:43

Yeah, in fact I played with it in the context of the tabbed interface cookbook recipe

tony.kay17:10:52

(not saved there...but that was the code I was using)

tony.kay17:10:52

that isn't the way I'd do it, though

tony.kay17:10:08

(df/load :all-profiles Profile {}) would be better (I need to make that last arg optional). Normalization will make the db table.

tony.kay17:10:43

so, file an issue and I'll see what I did wrong, but consider the technique...loading a single thing is what the ident syntax is meant to do

tony.kay17:10:55

With my suggested technique: the server then just returns a vector of profiles in resp to :all-profiles

gardnervickers17:10:45

What would you do if you had different lists of profiles

tony.kay17:10:58

do they all normalize the the same table, or different?

tony.kay17:10:31

add a post-mutation that creates your various filtered list of idents, which is what you must be doing already, right?

tony.kay17:10:36

if you want to load sub-lists: (df/load :profiles Profile {:params {:list :x-profiles}})

tony.kay17:10:54

(df/load :profiles Profile {:params {:list :all}})

tony.kay17:10:06

then the server will the the list parameter in params

tony.kay17:10:49

If you're loading sublists, then you can use target to place those ident lists where you want them!

tony.kay17:10:17

(df/load :profiles Profile {:params {:list :x-profiles} :target [:profile-widget :x :items]})

tony.kay17:10:33

obviating the need for post mutations 🙂

gardnervickers17:10:44

ahhh that makes sense now

gardnervickers17:10:48

that’s pretty great

tony.kay17:10:52

then you never need :all-profiles at all

tony.kay17:10:56

just issue 6-7 loads

tony.kay17:10:13

they'll all get batched together as a single net request by the back-end unless you specify :parallel true

tony.kay17:10:37

yeah, it is pretty sweet

tony.kay17:10:51

AND the load markers will go in the right places 🙂

tony.kay17:10:55

with target

gardnervickers17:10:16

That’s really cool, I was getting tripped up on the purpose of ident in a query server side

tony.kay17:10:34

yeah, that is for loading A specific entity/row of a database

tony.kay17:10:43

and putting in your app state table

tony.kay17:10:01

(df/load [:person/by-id 23] Person {})

gardnervickers17:10:42

Ahhh great, this removes a lot of the awkwardness I had going on before

tony.kay17:10:43

Now that I think of it, using :all in that should not work. We use Om's merge for that, and it expects a SINGLE thing on an ident merge. Not sure why that was working for you before actually

tony.kay17:10:30

entries in Om tables are supposed to be maps

gardnervickers17:10:43

For my [profiles-list/by-id :all] I was returning a thing like {:profile-list/by-id :all :list-of/profiles [….]}

tony.kay17:10:08

yeah, you were tricking normalization to do it

tony.kay17:10:24

so, that will remove a lot of awkwardness for sure 🙂

tony.kay17:10:24

the old joke of "doc, it hurts when I do X"....doc: "well, then don't do that"

gardnervickers17:10:28

heh for sure, thanks!

tony.kay17:10:11

@gardnervickers Just pushed a new snapshot to clojars. Makes config parameter optional

gardnervickers17:10:51

Great, I was going to ask about switching from the multi-arity options to the map options, glad it’s optional now.

tony.kay17:10:41

The named parameter syntax was nice on the surface, but I kept running into situations where I wanted to compose something I had in a map with the "options", which is quite ugly when functions take named parameter multi-arity. An optional map is just easier to work with compositionally.

tony.kay17:10:44

I'm acutally open to feedback. I don't consider the API of load solid yet. If you'd prefer the "named parameter" notation, it is a simple-enough change.

tony.kay17:10:32

I was more changing the internals around to use maps for cleaner stuff internally, and I propagated it out to the public API. Might make sense for the public API to do what load-data did with params

tony.kay17:10:07

I've actually never needed the composition aspect at the public layer...so I'm an easy sell 🙂

gardnervickers17:10:34

I am all for map params

adambros17:10:41

+1, its easier to create and work with programmatically, and as an end user you just have to now type 2 extra chars {…} (one char if you are using a good structural editing plugin)

gardnervickers18:10:05

Also wanted to mention how awesome the devcard integration is now for viewing app state

gardnervickers18:10:53

It would be pretty cool if the client included a version of the tutorial macro for using devcards with Untangled.

wilkerlucio20:10:00

@gardnervickers is there a place I can read more about this devcards integration?

gardnervickers20:10:12

Just checkout the tutorial

wilkerlucio20:10:44

thanks, found it 🙂

tony.kay20:10:13

Yeah, I should move the macro over

tony.kay20:10:20

or anyone else can....PR welcome 🙂

adambros20:10:30

@currentoor @jasonjckn @cjmurphy @therabidbanana @wilkerlucio and anyone who is using this in production: Tony and I have been talking about using clojure(script) 1.9 for clojure.spec The only question remaining is if you (the untangled community) feel that it would be safe for production use. So, would you be okay using 1.9?

currentoor20:10:56

@adambros we already do 😅

currentoor20:10:15

So we'd be more than fine with it!

adambros20:10:33

I'm wanting to spec and conform all the things, so thats great to hear.

currentoor20:10:53

We still haven't gotten around to using spec though. Looking forward to what you come up with.

adambros20:10:11

most of it is internal, but it should help with error messages and simplifying the codebase

currentoor20:10:41

I wrote a blog post about how we used this stack to build real-time collaboration in our product.

currentoor20:10:30

for the most part it goes outside untangled but I thought I'd share here in case anyone was interested

adambros20:10:31

nice stuff (skimmed it)

adambros20:10:26

i should write up how i did authorization in one of our products

currentoor20:10:44

at a high level what did you do?

currentoor20:10:51

we just used JWT tokens

adambros20:10:00

yeah im not talking about authentication

adambros20:10:08

we’re just using openid and jwt, iirc

adambros20:10:06

Authorization wise Entities (or one of their parents) have an :auth/owner ref An :auth/owner has a :auth/property that points to our auth database (we limit access by property or property group)

adambros20:10:38

the server code just walks up the graph from an entity until it can find an :auth/owner

adambros20:10:51

and checks you are allowed to read or mutate it

adambros20:10:26

we’d probably have to add more fields if we need more granular (user level) access control

adambros20:10:59

but it feels pretty flexible, and we dont have to pepper every entity with :auth/owner as some things can be inferred to be owned by one of their ancestors

wilkerlucio20:10:39

@adambros I'm using 1.9 on my project as well 🙂

adambros20:10:05

I’m going to go ahead and upgrade to 1.9 on my untangled branches

currentoor21:10:03

@adambros oh cool, that sounds more sophisticated than we we did

adambros21:10:47

it felt sophisticated, but i wasnt sure how to build something that would stay flexible as it was up in the air what kind of auth we would really need for it

currentoor21:10:23

we just wrote a macro that looks like a defmethod for om next mutations except it also takes in a policy function that takes in the arguments to the om next mutation and returns true or false based on authoraization

(defusecase api-mutate 'widget/delete with policy/widget-owner
  [{:keys [database]} _ {:keys [id]}]
  {:action #(delete-entity database id)})

(defn widget-owner
  "current/organization owns this widget."
  [env k params]
  (let [{:keys [database current/organization]} env
        {:keys [id]}                            params
        org-id                                  (:organization/adstage-id organization)
        conn                                    (db/get-connection database)]
    (and (existence env k params)
         (= org-id
            (-> (d/entity (d/db conn) id)
                :dashboard/_widgets
                :dashboard/organization
                :organization/adstage-id)))))

currentoor21:10:04

but i think our authorization is less complicated than yours because you have people doing surveys right?

adambros21:10:26

yeah but not as complicated as you might think if you can constrain it just right

adambros21:10:44

our concepts of being in a property helps a lot

adambros21:10:50

you also dont have to secure taking the survey

adambros21:10:02

they’re not logged in anyway

currentoor21:10:24

but yeah a blog post on it would be cool

adambros21:10:15

is there a reason you are destructuring in a let instead of your params? in widget-owner

adambros21:10:38

as a quick note, there’s a PR on untangled-datomic https://github.com/untangled-web/untangled-datomic/pull/4 for: 1. Implemented IDeref on DatabaseComponent, which returns a datomic.db.Db instance. 2. Updated the query function in untangled.datomic.core to support multiple data sources of varied types.

adambros21:10:19

let me (or @ethangracer) know if you have any thoughts

currentoor21:10:49

@adambros sloppy code review most likely 😅 (why destructuring inside let)

adambros21:10:24

haha lol its fine just curious

cjmurphy22:10:14

@adambros: Yes 1.9 will be good.

jasonjckn22:10:50

@adambros thanks for asking, yes! we upgraded last week for the same reason: spec