Skip to content

Instantly share code, notes, and snippets.

@souenzzo

souenzzo/app.clj Secret

Last active April 20, 2023 11:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save souenzzo/113795e3a7ce069417e5cb83c99aee9b to your computer and use it in GitHub Desktop.
Save souenzzo/113795e3a7ce069417e5cb83c99aee9b to your computer and use it in GitHub Desktop.
Ingá PREVIEW
(ns counter.app
(:require [app.inga :as inga]
[app.inga.ui :as inga.ui]
[com.wsscode.pathom.connect :as pc]
[io.pedestal.http.route :as route]
[io.pedestal.http :as http]))
;; model stuff
(pc/defresolver count-resolver [{:my-app/keys [state]} input]
{::pc/output [:my-app/count]}
{:my-app/count @state})
(pc/defmutation inc-mutation [{:my-app/keys [state]} args]
{::pc/sym `my-app/inc-mutation
::pc/output [:my-app/count]}
{:my-app/count (swap! state inc)})
;; view stuff
(pc/defresolver router [app args]
{::pc/output [::inga/title
::inga/icon
::inga/router]}
{::inga/title "Ingá App"
::inga/icon "data:image/x-icon;,"
::inga/router {:my-app.page/hello {::inga/path "/hello"
::inga/contents [{::inga/render ::inga.ui/query
::inga/display-properties [:my-app/count]}
{::inga/render ::inga.ui/mutation
::inga/mutation `my-app/inc-mutation}]}}})
(defn register
[]
[router inc-mutation count-resolver])
(defonce state (atom 0))
(def routes
(inga/export {:my-app/state state
::inga/register register}))
(def service
{:env :dev
::http/port 8081
::http/type :jetty
::http/routes #(route/expand-routes routes)
::http/join? false})
(defonce http-service (atom nil))
(defn -main
[& _]
(swap! http-service (fn [st]
(when st
(http/stop st))
(-> service
http/default-interceptors
http/dev-interceptors
http/create-server
http/start))))

Ingá

A data-driven scaffolding library.

This library aims to allow you to build a frontend that both show data and promps mutations basead on your Graph API.

Inspirations:

Targets

  • Basead on EQL

  • Extensible via Pathom connect

  • No naming conventions

  • Separation between task's (how to get data) and ui's (how to show data)

  • Flow semanticweb standards

Usage

  • TIP: it's based in src/sample/counter/app.clj. You can run with clj -A:dev -m counter.app

In the end, we want to have:

  1. A simple and easy frontend to interact with the app
  2. A minimal REST API

Pathom API

Let's build a simple "counter" app. Starting with a basic pathom connect API

;; [com.wsscode.pathom.connect :as pc]
(pc/defresolver count-resolver [{:my-app/keys [state]} input]
  {::pc/output [:my-app/count]}
  {:my-app/count @state})

(pc/defmutation inc-mutation [{:my-app/keys [state]} args]
  {::pc/sym    `inc-mutation
   ::pc/output [:my-app/count]}
  {:my-app/count (swap! state inc)})

Rest API

The inga/export function will generate a hash-set of pedestal routes.

;; [app.inga :as inga]
;; [io.pedestal.http :as http]
(defn register
  []
  [inc-mutation count-resolver])

(defonce state (atom 0))

(def routes
  (inga/export {:my-app/state   state
                ::inga/register register}))

(def service
  {:env          :dev
   ::http/port   8081
   ::http/type   :jetty
   ::http/routes #(route/expand-routes routes)
   ::http/join?  false})

You can start this pedestal service from REPL with something like (-> service http/default-interceptors http/dev-interceptors http/create-server http/start).

At this stage, our rest API is already working.

$ curl http://localhost:8081/my-app/count
0
$ curl -X POST http://localhost:8081/my-app/inc-mutation
## will return 202 Accepted by default
$ curl http://localhost:8081/my-app/count
1

Frontend

Now let's define a page which shows some arbitrary data

;; [app.inga :as inga]
;; [app.inga.ui :as inga.ui]

{::inga/path     "/hello"
 ::inga/contents [{::inga/render             ::inga.ui/query
                   ::inga/display-properties [:my-app/count]}
                  {::inga/render   ::inga.ui/mutation
                   ::inga/mutation `my-app/inc-mutation}]}
attribute value description
::inga/path "/hello" An arbitrary "route" that will show up in browser
::inga/contents [...] The list of components which we will use
::inga/render ::inga.ui/query The current UI implementation of the component.

Any other key, like ::inga/display-properties [:my-app/count] will be used by the render implementation.

In this case, it's saying to query task with keys you would like to show

Now we will put it inside a resolver and conj into the app registry

;; [io.pedestal.http.route :as route]
(pc/defresolver router [app args]
  {::pc/output [::inga/title
                ::inga/icon
                ::inga/router]}
  {::inga/title  "Ingá App"
   ::inga/icon   "data:image/x-icon;,"
   ::inga/router {:my-app.page/hello {::inga/path     "/hello"
                                      ::inga/contents [{::inga/render             ::inga.ui/query
                                                        ::inga/display-properties [:my-app/count]}
                                                       {::inga/render   ::inga.ui/mutation
                                                        ::inga/mutation `my-app/inc-mutation}]}}})

(defn register
  []
  [router inc-mutation count-resolver])

As you can see,::inga/router is returned by a resolver, so you can dynamically generate it based on anything.

At this point, you can connect to hello and you will see a table with :counter.app/count 27 and a form with counter.app/inc-mutation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment