with
Datomic, Ring, Om, & Garden
Priyatam Mudivarti
Principal, Facjure LLC
LambdaConf
Boulder, CO 2015
Templates
Database Modeling
Http Apis
Responsive Design
UI/Events
Services
All HTTP Requests must go through api defaults used by the industry. Use decent static site defaults, too.
Let me write a higher order function
What is the history of changes on this article id as-of Dec 25th?
?
Make the headings serifs and scale-types using four breakpoints (480 px increments). Use our brand settings on a 3-column grid
Let me write a higher order function
[
{:poem/title "I am not a hacker"}
{:poem/book :poets-and-hackers}
{:poem/year 2015}
{:author/name "Priyatam Mudivarti"}
{:author/email "priyatam@facjure.com"}
{:author/gender :male}
{:tag/names #{:hackers :poetry-slam :boulder-cafe :fp-rocks}
{[1 2] :one-two }}
][;; A user writing an article
{:db/id #db/id [:db.part/user -100]
:user/username "john.smith"}
{:db/id #db/id [:db.part/user -200]
:category/name "Functional Programming"}
{:db/id #db/id [:db.part/user -300]
:article/title "Monads in Pictures"
:article/author #db/id [:db.part/user -100]
:article/category #db/id [:db.part/user -200]
:article/body "http://bit.ly/14mj7WG"}
];; Requests and Response are immutable Maps
;; Handlers return Response
(defn what-is-my-ip [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body (:remote-addr request)})
;; Middlewares are higher-order functions that transform a handler
(defn wrap-content-type [handler content-type]
(fn [request]
(let [response (handler request)]
(assoc-in response [:headers "Content-Type"] content-type))))
;; Application
(def app
(-> what-is-my-ip
(wrap-content-type "text/html")
(wrap-keyword-params)
(wrap-params)))(html
[:li {:class (if active "active" "")}
[:a {:href (str "#" path)} name]])(defn view [data owner]
(reify
om/IRenderState
(render-state [_ {:keys [sample-text]}]
(let [text (:sample-text data)]
(html
[:section {:class "home"}
[:h1 text]
[:h2 text]
[:h3 text]
[:h4 text]
[:h5 text]
[:h6 text]
[:p.large text]
[:p.medium text]
[:p.small text]])))))
(defn clearfix [clazz]
[clazz {:*zoom 1}
[:&:before :&:after {:display "table"
:content " "
:line-height 0}]
[:&:after {:clear "both"}]])
functions that create sequences
functions that filter sequences
sequence predicates
functions that transform sequences
;; list comprehension
(for [x (range 1 10)
y (range 1 10)
:when (and (zero? (rem x y))
(even? (quot x y)))]
[x y])
;; higher order functions over sequences
(map inc [0 1 2 3 4 5 6 7 8 9])
(filter even? (range 10))
(reduce + (range 100))
(partition 2 [:a 1 :b 2 :c 3 :d 4 :e 5])
(remove odd? (range 10))
(take 5 (interleave (repeat "red") (repeat "blue")))
(map (comp - (partial + 3) (partial * 2)) [1 2 3 4]) ;;=> (-5 -7 -9 -11)
/type/author /authors/OL999930A 2 2008-08-20T17:56:50.993393 {"name": "Onur Akdog\u0306u", "personal_name": "Onur Akdog\u0306u", "last_modified": {"type": "/type/datetime", "value": "2008-08-20T17:56:50.993393"}, "key": "/authors/OL999930A", "type": {"key": "/type/author"}, "revision": 2}
*15 million books in open-data format
(defn parse-csv [str-in]
(csv/read-csv str-in :separator \tab))
(defn read-csv [data-in]
(with-open [f (io/reader (io/resource data-in))]
(doall (parse-csv f))))
(defn parse-json [data-in]
(->> data-in
(map #(nth % 4)) ;; 5th item is json data
(apply str)
(json/parse-string)
walk/keywordize-keys))
(defn to-data [res-in]
(with-open [res (io/reader (io/resource res-in))]
(->> res
(parse-csv)
(parse-json)
(timeify))))to this
datomic, ring, http
ring/compojure
http-kit / nginx
datomic/postgres*
dumb pipes,
smart transformations,
datalog queries
*even clj sql libraries treat adbc resultsets as Data
Datomic is an Information system.
Create Facts: Identity, Entities, Attributes, Values, Transactions
Find Facts: Query, DbFunctions, and Rules
Manage Facts: Peers, Transactors, Storage Engines
source: http://docs.datomic.com/architecture.html
Everything is a Fact: a 4-tuple datom created through a transaction, with implicit time
[entity attribute value transaction]
Queries and Rules are logic-constraints (expressed in where data clauses)
An append-only database—values don't change, time travel is free!
Database-functions can perform transactor-time operations
It's just function or higher-order function.
Middlewares decompose your problem into small reusable units
Adapts to most http-servers (sync/async) from a single abstraction
Best practices can be curated
content, styles, and async http responses as data
om/sablono
secretary
garden/mesh
core.async
global app-state with lenses-like cursors for safe data-access
use props for only transient state
plug routers, js libs
follow react life-cycle protocols
async event handling with core.async
no namespaces
can't compose HTML and CSS in the same syntax
css doesn’t “talk to dom”
math impl is hard
can we pass css as functions at runtime?
mixins are not composable
constrained by limitations—not by choice
programming stylesheets
in FP
separate selectors and declarations
Ring-style middleware
http://github.com/facjure/mesh
(defn create-minimal-grid [clazz pad]
[[:* {:box-sizing "border-box"}]
[clazz {:background "white"
:margin [[0 0 pad 0]]}
[:&:after {:content ""
:display "table"
:clear "both"}]
["[class*='col-']" {:float "left"
:padding-right pad }]
[:.col-1-3 {:width "33.33%"}]
[:.col-2-3 {:width "66.66%"}]
[:.col-1-2 {:width "50.00%"}]
[:.col-1-4 {:width "25.00%"}]
[:.col-1-8 {:width "12.50%"}]
[:.out-padding {:padding [[pad 0 pad pad pad]]}
["[class*='col-']:last-of-type"
{:padding-right pad}]]]])(defn modular-scale-fn [base ratio]
(let [[up down] (if (ratio? ratio)
(if (< (denominator ratio)
(numerator ratio))
[* /]
[/ *])
(if (< 1 ratio) [* /]
[/ *]))
f (float ratio)
us (iterate #(up % f) base)
ds (iterate #(down % f) base)]
(memoize
(fn ms [n]
(cond
(< 0 n) (if (whole-number? n)
(nth us n)
(let [m (Math/floor (float n))
[a b] [(ms m) (ms (inc m))]]
(+ a (* (Math/abs (- a b))
(- n m)))))
(< n 0) (if (whole-number? n)
(nth ds (Math/abs n))
(let [m (Math/floor (float n))
[a b] [(ms m) (ms (dec m))]]
(+ a (* (Math/abs (- a b))
(- n m)))))
:else base)))))
https://github.com/facjure/mesh
(def gutter (px 20))
(def typesetting
(list
(typo/typeset-body typo/defaults :golden)))
(def grids
(list (typo/baseline (rgba 0 0 255 0.5) (px 2))
(grid/create ".grid" gutter)
(grid/wrap-widths 978)
(grid/create-nested-units)
(grid/nuke-gutters-and-padding)
(grid/respond-small (:mobile breakpoints) gutter)
(grid/respond-medium (:tablet breakpoints))))
(def styles
(css (merge grids typesetting)))
Abstraction is at the center of most work in
Computer Science &
User Interface Design.
;; a bad example
(defn make-serifs [selector families]
(fn [declarations]
(let [styles (selector declarations)]
(conj styles (font (:garamond families) 3 600 0.5 2)))))
;; slightly better
(defn scale-type [selector params]
(fn [declarations]
(let [styles (selector declarations)]
(conj styles
(at-media {:min-width (get-in params [:breakpoints :mobile])}
[:& {:font-size (* 1.5 (:min-font params))}])
(at-media {:min-width (get-in params [:breakpoints :tablet])}
[:& {:font-size (* 1.75 (:min-font params))}])
(at-media {:min-width (get-in params [:breakpoints :laptop])}
[:& {:font-size (* 2.25 (:min-font params))}])))))
(defn headings [declarations]
[:h1 :h2])
(-> headings
(scale-type settings)
(make-serifs brand-fonts)
(make-sans-brand-fonts)
(grid/create {:columns 3})om, secretary, garden, mesh
Demos created with https://github.com/priyatam/mala https://github.com/priyatam/ring-micro
https://github.com/facjure/mesh https://github.com/facjure/atomic Libraries
https://github.com/ring-clojure
https://github.com/omcljs/om
https://github.com/noprompt/garden
https://github.com/noprompt/secretary
https://github.com/bhauman/lein-figwheel
https://slides.com/priyatam/fullstack-clj-cljs
@priyatam
priyatam@facjure.com
http://github.com/priyatam