Skip to content

Instantly share code, notes, and snippets.

@IwanKaramazow
Created June 12, 2016 15:09
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 IwanKaramazow/260e1f1c25bd0d4bc8a9eb7f910b8fa2 to your computer and use it in GitHub Desktop.
Save IwanKaramazow/260e1f1c25bd0d4bc8a9eb7f910b8fa2 to your computer and use it in GitHub Desktop.
Query Diffing!
(ns listdetail.core
(:require [goog.dom :as gdom]
[om.next :as om :refer-macros [defui]]
[om.dom :as dom]))
(enable-console-print!)
(def app-state {:projects [{:id 1 :title "Learn Haskell"} {:id 2 :title "Learn Scheme"}]})
;; parser
(defmulti read om/dispatch)
(defmethod read :projects
[{:keys [state query]} key _]
(let [st @state]
{:value (om/db->tree query (get st key) st)}))
(defmethod read :project/detail
[{:keys [ast state query]} key {:keys [id]}]
(when id
(let [st @state
val (om/db->tree query [:project/by-id id] st)
available (keys val)
diffed-query (->
(into [] (clojure.set/difference (set query) (set available)))
(conj :id))]
(if (= available query)
{:value val}
{:value val
:remote (assoc ast :query diffed-query)}))))
;; components
(defui ProjectListItem
static om/Ident
(ident [this {:keys [id]}]
[:project/by-id id])
static om/IQuery
(query [_]
[:id :title])
Object
(render [this]
(let [{:keys [id title]} (om/props this)
{:keys [select] } (om/get-computed this)]
(dom/li #js {:onClick (fn [e]
(select id))} title))))
(def projectListItem (om/factory ProjectListItem))
(defn project-list [projects select]
(dom/div nil
(dom/h3 nil "Project list:")
(dom/ul nil
(map #(projectListItem (om/computed % {:select select})) projects))))
(defui ProjectDetail
static om/Ident
(ident [this {:keys [id]}]
[:project/by-id id])
static om/IQuery
(query [_]
[:id :title :description])
Object
(render [this]
(let [{:keys [id title description]} (om/props this)]
(dom/div nil
(dom/h3 nil "Project Detail")
(if (nil? id)
(dom/div nil "No project selected.")
(dom/div nil
(dom/div nil "Selected project: " id)
(dom/div nil "Title: " title)
(dom/div nil "Description: " description)))))))
(def project-detail (om/factory ProjectDetail))
(defn select-project [component]
(fn [id]
(om/update-query! component
(fn [{:keys [query params] :as q}]
(update q :params assoc :id id)) )))
(defui Root
static om/IQueryParams
(params [this]
{:id nil})
static om/IQuery
(query [_]
`[{:projects ~(om/get-query ProjectListItem)}
({:project/detail ~(om/get-query ProjectDetail)} {:id ~'?id})])
Object
(render [this]
(let [{:keys [projects project/detail]} (om/props this)]
(dom/div nil
(project-list projects (select-project this))
(project-detail detail)))))
;; fake backend
(def fake-database (atom {:projects [[:project/by-id 1] [:project/by-id 2]], :project/by-id {1 {:id 1, :title "Learn Haskell" :description "How do I get started with Haskell?"}, 2 {:id 2, :title "Learn Scheme" :description "Lisp rocks!"}}}))
(defmulti backend-read om/dispatch)
(defmethod backend-read :project/detail
[{:keys [database query]} key {:keys [id]}]
(let [db @database]
{:value (om/db->tree query [:project/by-id id] db)}))
(def backend-parser (om/parser {:read backend-read}))
;; fake a send to the fake backend
(defn send [{:keys [remote]} merge]
(merge (backend-parser {:database fake-database} remote)))
;; do not throw away the app state when merging
(defn deep-merge [& xs]
"Merges nested maps without overwriting existing keys."
(if (every? map? xs)
(apply merge-with deep-merge xs)
(last xs)))
;;reconciler
(def reconciler
(om/reconciler
{:state app-state
:parser (om/parser {:read read})
:send send
:merge-tree deep-merge}))
(om/add-root! reconciler
Root (gdom/getElement "app"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment