untangled

NEW CHANNEL: #fulcro
tony.kay 2016-10-13T00:02:49.002403Z

0.6.0-SNAPSHOT is on clojars

tony.kay 2016-10-13T00:03:31.002404Z

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.kay 2016-10-13T00:03:51.002405Z

The target is also where the load marker will go

tony.kay 2016-10-13T00:04:56.002406Z

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"

2016-10-13T00:58:36.002407Z

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

2016-10-13T01:00:28.002408Z

@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

2016-10-13T01:01:04.002409Z

@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.kay 2016-10-13T01:16:31.002412Z

@jasonjckn Yeah, that latter use-case is one Iโ€™m still pondering

tony.kay 2016-10-13T01:17:13.002413Z

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.kay 2016-10-13T01:17:29.002414Z

we originally had load-collection and load-singleton

tony.kay 2016-10-13T01:18:38.002415Z

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.kay 2016-10-13T02:50:47.002416Z

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.kay 2016-10-13T02:51:43.002418Z

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

๐Ÿ‘ 1
gardnervickers 2016-10-13T17:23:09.002425Z

@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.kay 2016-10-13T17:24:06.002427Z

no, that should work

tony.kay 2016-10-13T17:24:26.002428Z

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

tony.kay 2016-10-13T17:24:43.002429Z

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

tony.kay 2016-10-13T17:24:52.002430Z

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

tony.kay 2016-10-13T17:26:17.002431Z

@gardnervickers ^^^^

gardnervickers 2016-10-13T17:26:45.002432Z

Hmm ok thanks

tony.kay 2016-10-13T17:26:52.002433Z

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

tony.kay 2016-10-13T17:28:08.002434Z

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

tony.kay 2016-10-13T17:28:43.002435Z

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.kay 2016-10-13T17:29:55.002436Z

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

gardnervickers 2016-10-13T17:30:45.002439Z

What would you do if you had different lists of profiles

tony.kay 2016-10-13T17:30:58.002440Z

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

gardnervickers 2016-10-13T17:31:03.002441Z

The same

tony.kay 2016-10-13T17:31:31.002442Z

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

tony.kay 2016-10-13T17:32:36.002443Z

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

tony.kay 2016-10-13T17:32:54.002444Z

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

tony.kay 2016-10-13T17:32:55.002445Z

etc

tony.kay 2016-10-13T17:33:06.002446Z

then the server will the the list parameter in params

tony.kay 2016-10-13T17:33:49.002447Z

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

tony.kay 2016-10-13T17:34:17.002448Z

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

tony.kay 2016-10-13T17:34:33.002449Z

obviating the need for post mutations ๐Ÿ™‚

gardnervickers 2016-10-13T17:34:44.002450Z

ahhh that makes sense now

gardnervickers 2016-10-13T17:34:48.002451Z

thatโ€™s pretty great

tony.kay 2016-10-13T17:34:52.002452Z

then you never need :all-profiles at all

tony.kay 2016-10-13T17:34:56.002453Z

just issue 6-7 loads

tony.kay 2016-10-13T17:35:13.002454Z

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

gardnervickers 2016-10-13T17:35:30.002456Z

fantastic

tony.kay 2016-10-13T17:35:37.002457Z

yeah, it is pretty sweet

tony.kay 2016-10-13T17:35:51.002458Z

AND the load markers will go in the right places ๐Ÿ™‚

tony.kay 2016-10-13T17:35:55.002459Z

with target

gardnervickers 2016-10-13T17:36:16.002460Z

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

tony.kay 2016-10-13T17:36:34.002461Z

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

tony.kay 2016-10-13T17:36:43.002462Z

and putting in your app state table

gardnervickers 2016-10-13T17:36:48.002463Z

gotcha

tony.kay 2016-10-13T17:37:01.002464Z

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

gardnervickers 2016-10-13T17:37:42.002465Z

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

tony.kay 2016-10-13T17:37:43.002466Z

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.kay 2016-10-13T17:38:30.002469Z

entries in Om tables are supposed to be maps

gardnervickers 2016-10-13T17:38:43.002470Z

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

tony.kay 2016-10-13T17:39:00.002471Z

AH

tony.kay 2016-10-13T17:39:08.002472Z

yeah, you were tricking normalization to do it

tony.kay 2016-10-13T17:39:24.002473Z

so, that will remove a lot of awkwardness for sure ๐Ÿ™‚

tony.kay 2016-10-13T17:40:24.002474Z

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

gardnervickers 2016-10-13T17:41:28.002475Z

heh for sure, thanks!

tony.kay 2016-10-13T17:41:35.002476Z

welcome

tony.kay 2016-10-13T17:47:11.002477Z

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

gardnervickers 2016-10-13T17:47:51.002478Z

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

tony.kay 2016-10-13T17:49:41.002479Z

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.kay 2016-10-13T17:51:44.002480Z

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.kay 2016-10-13T17:52:32.002481Z

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.kay 2016-10-13T17:53:07.002482Z

I've actually never needed the composition aspect at the public layer...so I'm an easy sell ๐Ÿ™‚

gardnervickers 2016-10-13T17:53:34.002483Z

I am all for map params

adambros 2016-10-13T17:54:41.002484Z

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

tony.kay 2016-10-13T17:55:56.002485Z

k

gardnervickers 2016-10-13T18:14:05.002487Z

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

๐Ÿ‘ 2
gardnervickers 2016-10-13T18:14:53.002488Z

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

๐Ÿ‘ 2
wilkerlucio 2016-10-13T20:22:00.002491Z

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

gardnervickers 2016-10-13T20:22:12.002492Z

Just checkout the tutorial

wilkerlucio 2016-10-13T20:25:44.002493Z

thanks, found it ๐Ÿ™‚

tony.kay 2016-10-13T20:26:13.002494Z

Yeah, I should move the macro over

tony.kay 2016-10-13T20:26:20.002495Z

or anyone else can....PR welcome ๐Ÿ™‚

adambros 2016-10-13T20:28:30.002496Z

@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?

currentoor 2016-10-13T20:28:56.002497Z

@adambros we already do ๐Ÿ˜…

adambros 2016-10-13T20:29:11.002498Z

:3

currentoor 2016-10-13T20:29:15.002499Z

So we'd be more than fine with it!

adambros 2016-10-13T20:29:33.002500Z

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

currentoor 2016-10-13T20:32:53.002503Z

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

adambros 2016-10-13T20:33:11.002504Z

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

currentoor 2016-10-13T20:33:41.002506Z

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

currentoor 2016-10-13T20:34:30.002509Z

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

adambros 2016-10-13T20:34:31.002510Z

nice stuff (skimmed it)

adambros 2016-10-13T20:35:26.002511Z

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

currentoor 2016-10-13T20:35:44.002512Z

at a high level what did you do?

currentoor 2016-10-13T20:36:51.002513Z

we just used JWT tokens

adambros 2016-10-13T20:37:00.002514Z

yeah im not talking about authentication

adambros 2016-10-13T20:37:08.002515Z

weโ€™re just using openid and jwt, iirc

gardnervickers 2016-10-13T20:37:35.002517Z

We use 1.9

๐Ÿ‘ 1
adambros 2016-10-13T20:39:06.002518Z

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)

adambros 2016-10-13T20:39:38.002519Z

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

adambros 2016-10-13T20:39:51.002520Z

and checks you are allowed to read or mutate it

adambros 2016-10-13T20:40:26.002521Z

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

adambros 2016-10-13T20:40:59.002522Z

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

wilkerlucio 2016-10-13T20:42:39.002524Z

@adambros I'm using 1.9 on my project as well ๐Ÿ™‚

๐Ÿ‘ 1
adambros 2016-10-13T20:43:05.002525Z

Iโ€™m going to go ahead and upgrade to 1.9 on my untangled branches

currentoor 2016-10-13T21:08:03.002526Z

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

adambros 2016-10-13T21:08:47.002527Z

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

currentoor 2016-10-13T21:10:23.002529Z

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

currentoor 2016-10-13T21:11:04.002530Z

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

adambros 2016-10-13T21:11:26.002531Z

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

adambros 2016-10-13T21:11:44.002532Z

our concepts of being in a property helps a lot

adambros 2016-10-13T21:11:50.002533Z

you also dont have to secure taking the survey

adambros 2016-10-13T21:12:02.002534Z

theyโ€™re not logged in anyway

currentoor 2016-10-13T21:12:15.002535Z

oh i see

currentoor 2016-10-13T21:12:24.002536Z

but yeah a blog post on it would be cool

adambros 2016-10-13T21:13:15.002537Z

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

adambros 2016-10-13T21:14:38.002538Z

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.

adambros 2016-10-13T21:15:19.002540Z

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

currentoor 2016-10-13T21:46:49.002541Z

@adambros sloppy code review most likely ๐Ÿ˜… (why destructuring inside let)

adambros 2016-10-13T21:47:24.002543Z

haha lol its fine just curious

cjmurphy 2016-10-13T22:00:14.002544Z

@adambros: Yes 1.9 will be good.

๐Ÿ‘ 1
2016-10-13T22:30:50.002545Z

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

๐Ÿ’ฅ 1