pathom

:pathom: https://github.com/wilkerlucio/pathom/ & https://pathom3.wsscode.com & https://roamresearch.com/#/app/wsscode
2020-05-29T12:52:03.116200Z

Anyone aware of a Pathom resolver that allows to walk the file system or a git repo?

2020-05-29T12:59:00.118700Z

I've heard of none, but what's the use case? Putting a pathom interface in front of things may or may not add any value

2020-05-29T12:59:44.119400Z

It’s a very specific one 🙂

2020-05-29T13:00:03.119800Z

I want to be able to browse files in my new web framework haha

2020-05-29T13:00:46.120500Z

I want to do this without leaking implementation details in my view (the whole reason of using Pathom I believe)

2020-05-29T13:01:06.120900Z

So i was thinking of starting with something simple like a file browser

2020-05-29T13:01:23.121300Z

Maybe someone else was crazy enough to do this as well

2020-05-29T13:01:31.121600Z

I guess not 🙈

2020-05-29T14:00:00.121900Z

Any feedback on this? I want it to work recursively, but I’m still trying to get that to work

wilkerlucio 2020-05-29T14:05:12.123300Z

@jeroenvandijk hello, first I like to suggest you to use the simple parser instead of parallel, will be faster for this case. And I guess looking at the code that you can do recursive calls already, you can try this query:

(clojure.pprint/pprint
 (async/<!! (parser {::p/entity {:file/path "."}}
                    '[:file/path
                      :file/type
                      {:dir/files ...})))

2020-05-29T14:07:30.124600Z

thank you! It’s not resolving the :dir/files recursively. I think I should add some join functionality somewhere. Reading that documentation now, but it didn’t click yet for me

2020-05-29T14:11:14.125400Z

This is the output. I would expect/hope ./src and ./git to be expanded

{:file/path ".",
 ... nil,
 :file/type :dir,
 :dir/files
 [#:file{:path "./project.clj"}
  #:file{:path "./.gitignore"}
  #:file{:path "./.nrepl-history"}
  #:file{:path "./.git"}
  #:file{:path "./src"}]}

wilkerlucio 2020-05-29T14:14:27.125800Z

@jeroenvandijk I wrote my version of it, this works:

(pc/defresolver file-resolver [env {:file/keys [path]}]
  {::pc/input  #{:file/path}
   ::pc/output [:file/type]}
  (let [f    (<http://clojure.java.io/file|clojure.java.io/file> path)
        dir? (.isDirectory f)]
    {:file/type (if dir? :dir :file)
     :file/dir? dir?}))

(pc/defresolver directory-files-resolver [env {:file/keys [path dir?]}]
  {::pc/input  #{:file/path :file/dir?}
   ::pc/output [{:dir/files [:file/path]}]}
  (if dir?
    {:dir/files
     (mapv (fn [^File f0] {:file/path (.getPath f0)})
       (.listFiles (<http://clojure.java.io/file|clojure.java.io/file> path)))}))

(def my-resolvers [file-resolver directory-files-resolver])

(def parser
  (p/parser
    {::p/env     {::p/reader               [p/map-reader
                                            pc/reader2
                                            pc/open-ident-reader
                                            p/env-placeholder-reader]
                  ::p/placeholder-prefixes #{"&gt;"}}
     ::p/mutate  pc/mutate
     ::p/plugins [(pc/connect-plugin {::pc/register my-resolvers})
                  p/error-handler-plugin
                  p/request-cache-plugin
                  p/trace-plugin]}))

(comment
  (parser {} [{[:file/path "src"]
               [:file/path
                :file/type
                {:dir/files '...}]}]))

👍 1
2020-05-29T14:17:43.126400Z

wow @wilkerlucio thank you!

2020-05-29T14:18:33.127200Z

So I guess my version didn’t trigger the recursion because :dir/files was already in the returning map, right?

wilkerlucio 2020-05-29T14:19:49.127700Z

I don't think that's the issue, your code seems like it should work

wilkerlucio 2020-05-29T14:19:54.127900Z

but your initial query example was off

wilkerlucio 2020-05-29T14:20:04.128200Z

was missing the map on the join for :dir/files

wilkerlucio 2020-05-29T14:20:14.128400Z

:dir/files [* {:dir/files [*]}]

wilkerlucio 2020-05-29T14:20:24.128800Z

☝️ missing {} around

2020-05-29T14:21:26.129600Z

Ah thank you. Good to understand this 🙂

2020-05-29T14:23:10.130Z

you are right, my query was wrong. The resolver was working

2020-05-29T14:29:40.131400Z

I guess it is in general a best practise to make your resolvers as small as possible. For reusability and maybe also for performance?

2020-05-29T14:34:42.132Z

From the docs:

Pathom will scan through the defined resolvers in order to try to satisfy all of the properties in a query. So, technically you can split up your queries as much as makes sense into separate resolvers, and as long as the inputs are in the context Pathom will assemble things back together.
I guess my resolver was indeed not lazy enough

2020-05-29T14:35:00.132500Z

Thanks @wilkerlucio. You have created an amazing library. Looking forward to use it more

2020-05-29T14:51:31.133900Z

Is it possible to have context dependent resolvers? E.g. when I want to browse a git tree {:dir/files …} would need to trigger a different resolver than when I browse a normal file system. I could also use a different key for that, but it would require more knowledge of the user

2020-05-29T14:53:50.134300Z

I think I have an idea 🙂

2020-05-29T14:56:17.135200Z

I think this will work when I implemented

(pc/defresolver file-resolver [{:keys [:resolvers.file/root-dir] :as env} {:file/keys [path]}]
  {::pc/input  #{:file/path}
   ::pc/output [:file/type]}
  (let [f    (<http://clojure.java.io/file|clojure.java.io/file> (str root-dir) path)
        dir? (.isDirectory f)]
    {:file/type (if dir? :dir :file)
     :file/dir? dir?
     :filesystem/type :normal}))


(pc/defresolver directory-files-resolver [{:keys [:resolvers.file/root-dir] :as env} {:file/keys [path dir?] filesystem-type :filesystem/type}]
  {::pc/input  #{:file/path :file/dir? :filesystem/type}
   ::pc/output [:dir/files]}
  (let [relativy (if root-dir
                   (let [idx (count root-dir)]
                     (fn [path]
                       (subs path idx)))
                   identity)]
    (if (= filesystem-type :normal)
      {:dir/files
       (when dir?
         (mapv (fn [^java.io.File f0] {:file/path (relativy (.getPath f0))})
               (.listFiles (<http://clojure.java.io/file|clojure.java.io/file> root-dir path))))}
      (throw (ex-info "not implemented yet" {}))
      )))
When I write a different resolver for a git filesystem the user doesn’t have to know it’s querying something different 🙂

wilkerlucio 2020-05-29T15:26:46.135900Z

@jeroenvandijk you can just make multiple resolvers for the same key as well, and in the case out of context you return nil on the resolver, so pathom will try the other one

2020-05-29T15:27:15.136100Z

Ah awesome!

Reily Siegel 2020-05-29T15:54:08.141800Z

Hello, is there an acceptable way to access the parser from within a mutation? I have two use cases for this: 1. Resolve unknown information that a mutation needs to function, that the caller of the mutation does not know. 2. Have a mutation submit several other "sub-mutations" to make smaller, reusable mutations For context, my problem domain is interfacing with the Discord API. So for example, for a higher level feature, I might need to make several "sub-mutations" (create post, add a reaction, etc). I also dont want to burden callers of the mutation with knowing details. For example, instead of requiring the "create post" mutation to accept a :discord.channel/id parameter, it would be more convenient to accept a :discord.channel/name and resolve the id based on an existing resolver. Am I thinking about this completely wrong?

2020-05-29T15:55:41.142300Z

@reilysiegel there's :parser key in env already

Reily Siegel 2020-05-29T15:58:00.145400Z

Thank you! So in that case the solution to 2 is obvious, and for 1 I could pass the parameter map as the context to the resolver, and the desired parameters as a query.

Reily Siegel 2020-05-29T15:58:46.146Z

In fact, I could probably write a transform that automatically does that based on ::pc/params

2020-05-29T16:02:50.147300Z

a note to consider sub-mutations vs one composite mutation if you're using fulcro: each individual mutation may receive some data from server so your client db is updated - all the updates may be hard to denoted with a composite mutation

2020-05-29T16:03:27.148Z

also, sub-mutations can "co-ordinate" by using tempids

2020-05-29T16:04:15.148700Z

anyway, both ways are valid, just evaluate the tradeoffs

kenny 2020-05-29T17:04:49.151600Z

I was going to try out the latest pathom release 2.3.0-alpha9. Upon updating to that release from 2.2.30, I receive an exception trying to load a ns that uses pathom:

Syntax error (ClassNotFoundException) compiling at (com/fulcrologic/guardrails/config.cljc:17:1).
com.google.javascript.jscomp.CompilerOptions
From the changelog I saw 2.3.0-alpha4 bumped the dep that appears to be causing this issue - guardrails. I tried running with 2.3.0-alpha4 instead and received the same issue. I went all the way back to 2.3.0-alpha1 and still get the exception.

2020-05-29T17:23:19.152800Z

@kenny just a generic workaround: have you tried :exclusions in your dependencies?

kenny 2020-05-29T17:24:25.154Z

Wasn’t worth the time to try it out. That’s probably the solution though. One of pathom’s deps updated and now conflicts with something else.

wilkerlucio 2020-05-29T17:56:59.154600Z

@kenny I wonder if has something to do with cljs versions itself, are you running on a recent one?

kenny 2020-05-29T18:03:28.154700Z

This isn't on a cljs app. It's a dep on a Clojure backend service.

kenny 2020-05-29T18:04:09.154900Z

I suppose cljs could be brought in accidentally.

wilkerlucio 2020-05-29T18:12:08.155100Z

maybe, what makes me think around that is because the error seems go be around some missing google closure thing

wilkerlucio 2020-05-29T18:12:33.155300Z

and from the deps jump you did, guardrails is a new dep

kenny 2020-05-29T18:12:47.155500Z

The line of code the exception is pointing to also has this comment:

;; This isn't particularly pretty, but it's how we avoid
;; having ClojureScript as a required dependency on Clojure