Skip to content

Instantly share code, notes, and snippets.

@abp
Last active December 13, 2015 19:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save abp/4966883 to your computer and use it in GitHub Desktop.
Save abp/4966883 to your computer and use it in GitHub Desktop.
Every request is an endpoint to start some computation.
The input is the request, the output is the response.
Typical stages between those are:
Parsing, validation, storage, retrieving stored data and rendering an output.
Based on Ring and Compojure, we do the following to integrate routes with
Prismatic Graph:
Compojure matches the request against routes, calling the route handlers,
until a non nil result is returned.
Routes for integration with the Graph return route-keys,
singular :key or nested like [:some :key].
A graph computation is then invoked, to calculate a result
for the given route-keys, based on the request.
Due to the declarative nature of the graph, the route now *depends* on
it's outputs, rather than explicitly needing to build them.
To separate routes that dispatch onto the graph from regular ones, there
is a wrap-graph-dispatch middleware. It takes up to three parameters:
handler - We know this one.
app-graph - The graph to invoke.
context-keys - Optional. Keys to be prepended to route-keys in this context.
(think context-keys [:context :to] route-keys [:some :key]
=> [:context :to :some :key]).
Now we can compute the route from the graph, by feeding the request data
to the graph, pulling out the route-keys as result.
When compiled for lazy excecution, the graph will only resolve
route-keys dependencies.
If the computation returns nil, the next routes are matched, just like
with regular Compojure routes.
(ns web-grapp
(:require [plumbing.core :refer :all]
[plumbing.graph :as graph]
[compojure.core :refer :all]
[compojure.response :refer [render]]
[hiccup.page :refer [html5]]
[robert.hooke :refer [with-scope add-hook]]))
(defn graph-dispatch [f ks request]
(get-in ((::graph request) request)
(concat (::context-keys request)
(cond-> ks
(nil? ks) vec
(keyword? ks) vector))))
(defn wrap-graph-dispatch
([handler app-graph]
(wrap-graph-dispatch handler app-graph nil))
([handler app-graph context-keys]
(let [context-keys (cond-> context-keys
(nil? context-keys) vec
(keyword? context-keys) vector)]
(fn [request]
(render
(with-scope
(add-hook #'render ::wrap-graph-dispatch #'graph-dispatch)
(handler (assoc request
::graph app-graph
::context-keys context-keys)))
request)))))
(def search-graph
{:db {:search (fnk [[:params q]]
["some" "results"])}
:view {:search-results (fnk [[:db search] [:params q]]
(list [:p "Results for query: " q]
[:ul (for [result search]
[:li result])]))}
:routes {:search (fnk [[:view search-results]]
(html5 search-results))}})
(def lazy-search (graph/lazy-compile search-graph))
;; (wrap-graph-dispatch (GET "/search" _ :search) lazy-search :routes))
(defroutevec search
[:routes
#get :search])
(search {:uri "/search" :request-method :get :params {:q "a query"}})
;; {:status 200,
;; :headers {"Content-Type" "text/html; charset=utf-8"},
;; :body
;; "<!DOCTYPE html>\n<html><p>Results for query: a query</p><ul><li>some</li><li>results</li></ul></html>"}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment