Clojure Buzzwords I am Learning
Software Engineering Team Lead and Director of Cloudsure
When I first started with Clojure, my mind was foggy and filled with a swarm of buzzwords. There were tools, plugins, libraries, patterns and frameworks with names that were all new to me. This is a blog post of all the buzzwords that I am learning.
I have been a .NET and SQL web developer for the last decade. I now find myself in the land of Infrastructure as a Service (IaaS) as a senior systems and web/full stack developer in completely new paradigms.
I am writing production and utility software in Clojure(Script) integrating with Datomic. I also have SysAdmin responsibilities where I configure monitoring, manage and provide support for our systems.
I've migrated to Ubuntu after twenty years of using Windows so it is safe to say that all these changes have stretched me outside my comfort zone - and I am absolutely loving it!
Disclaimer: I am only sharing my understanding of each. If I have misunderstood something, please jump in and submit a pull request.
Leiningen (Build automation)
Leiningen, pronounced (LINE-ing-en ['laɪnɪŋən], is a tool created by Phil Hagelberg. He wanted to simplify the complexities involved in creating a project with Apache Maven.
lein was born as a Clojure build automation and dependency management tool that can accept input from the terminal. You can create projects, fetch dependencies, run tests, create a REPL and do other cool things.
lein new app awesome-app
lein update
lein test
lein repl
On the front end, lein is used to replace JavaScript tooling such as:
Build step | Tools |
---|---|
Project scaffolding | Grunt, Slush, Yeoman |
Build tools | Webpack, Grunt, Gulp, Browserify |
Package managers | npm, yarn, bower |
This table is adapted from Why I chose ClojureScript over JavaScript by akiroz.
ClojureScript (Clojure to JavaScript)
Clojure targets platforms like the JVM and .NET CLR. ClojureScript is just a Clojure (without Java API calls) compiler targeting JavaScript. The JavaScript is compiled in such a way that it can be optimized using the Google Closure compiler.
Google Closure (JavaScript optimizer)
JavaScript libraries can become bloated with dead code, comments and require dependency management. Google Closure compiles JavaScript code with an optimization algorithm creating compact and high-performance code.
The compiler inspects variable references, checks syntax, types and warnings along with other common JavaScript idiosyncrasies. It then strips away dead code, rewrites what's left, minifies and packages a file that can be quickly downloaded and executed.
Datomic (Database)
Datomic is a non-traditional distributed database with ACID transactions, joins and a logical query language.
Datomic is a set of datoms which are atomic facts. They say if the relation between an entity, an attribute, a value, and a transaction has been added or retracted. A datom is expressed as a five-tuple:
- entity id (E)
- attribute (A)
- value (V)
- transaction id (Tx)
- addition / retraction boolean (Op)
E (ntity) | A(ttribute) | V(alue) | Tx | Op |
---|---|---|---|---|
42 | :user/first-name | "Douglas" | 1234 | true |
42 | :user/last-name | "Adams" | 1234 | true |
42 | :user/favorite-color | :emerald | 1234 | false |
42 | :user/favorite-color | :teal | 1235 | false |
42 | :user/favorite-color | :crimson | 1236 | true |
That can translate to the following point-in-time view as only a three-tuple with Tx and Op omitted:
E (ntity) | A(ttribute) | V(alue) |
---|---|---|
42 | :user/first-name | "Douglas" |
42 | :user/last-name | "Adams" |
42 | :user/favorite-color | :crimson |
{:db/id 42
:user/favorite-color :crimson
:user/first-name "Douglas"
:user/last-name "Adams"}
Reagent (React framework)
Reagent is a minimalistic ClojureScript interface to React.js. HTML components are represented as Clojure data following a hiccup-like syntax and is compiled to React components. Pssst. No more JSX files.
(defn home-panel []
[:div.hello-world
[:p
[:b "Hello World"]]])
<div data-reactroot="" class="hello-world">
<p><b>Hello World</b></p>
</div>
re-frame (React pattern)
re-frame is a Reagent Framework pattern for writing SPAs (Single Page Applications) in ClojureScript. It is built on the same principles of Redux.
It's a loop of what they refer to as dominoes. One domino triggers the next until it is ready for the next iteration of the same cascade.
Figwheel (Hot reloader)
Figwheel, a lein plugin, is a ClojureScript auto builder/server which pushes changed files to the browser.
Cutting some fruit, it says on load, it detects changes in the code, reloads the screen and keeps all the glorious state in the application at that time.
lein new figwheel hello-world -- --reagent #for a reagent based project
Garden (ClojureScript to CSS)
Garden generates CSS from ClojureScript data structures.
As I write this I am imagining a garden with ornamental green grasses, thick green plants and maybe a few flowers here and there. This beautiful garden seems symbolic of styling.
This imagined outdoor beauty can be digitized into Clojure vectors and maps written in a Hiccup inspired syntax to generate CSS to beautify your website.
(require '[garden.core :refer [css]])
(css [:body {:font-size "16px"}])
body {
font-size: 16px;
}
Compojure (Server-side routing)
Compojure is a small server-side routing library for Ring that allows web applications to be composed of small, independent parts.
lein new compojure hello-world
cd hello-world
lein ring server-headless
(ns hello-world.core
(:require [compojure.core :refer :all]
[compojure.route :as route]))
(defroutes app
(GET "/" [] "<h1>Hello World</h1>")
(route/not-found "<h1>Page not found</h1>"))
Secretary (Client-side routing)
Secretary is a client-side router for ClojureScript. It is built to create route matchers and dispatch actions.
(ns app.routes
(:require [secretary.core :as secretary :refer-macros [defroute]]))
(defroute "/users/:id" {:as params}
(js/console.log (str "User: " (:id params))))
(secretary/dispatch! "/users/gf3")
Pushy (HTML5 pushState)
Pushy is used for HTML5 pushState and can be integrated with routing libraries such as Secretary, Bidi, Silk, Router and Sibiro.
Here's an example implementation of an integration with Secretary:
(ns foo.core
(:require [secretary.core :as secretary :include-macros true :refer-macros [defroute]]
[pushy.core :as pushy]))
(secretary/set-config! :prefix "/")
(defroute index "/foo" []
(.log js/console "Hi"))
(def history (pushy/pushy secretary/dispatch!
(fn [x] (when (secretary/locate-route x) x))))
;; Start event listeners
(pushy/start! history)
Bouncer (Validation)
Bouncer is a validation DSL.
(ns some.ns
(:require [bouncer.core :as b]
[bouncer.validators :as v]))
(def person {:name "Leo"})
(b/validate person
:name v/required
:age v/required)
;; [{:age ("age must be present")}
;; {:name "Leo", :bouncer.core/errors {:age ("age must be present")}}]
Inflections (Word manipulation)
Inflections is a rails-like inflection library.
Inflection: A change in the form of a word (typically the ending) to express a grammatical function or attribute such as tense, mood, person, number, case, and gender.
(use 'inflections.core)
(plural "word")
;=> "words"
(plural "virus")
;=> "viri"
(pluralize 12 "virus")
;=> "12 viri"
(singular "apples")
;=> "apple"
(singular "octopi")
;=> "octopus"
(underscore "puni-puni")
;=> "puni_puni"
(ordinalize "52")
;=> "52nd"
(capitalize "clojure")
;=> "Clojure"
Humanize (Word manipulation)
Humanize produces human readable strings and dates.
user> (numberword 3567)
=> "three thousand five hundred and sixty-seven"
user> (clojure.contrib.humanize/intcomma 1000)
=> 1,000
user> (clojure.contrib.humanize/intword 2000000000)
=> 2.0 billion
user> (clojure.contrib.humanize/ordinal 111)
=> 111th
user> (clojure.contrib.humanize/filesize 3000000 :binary true)
=> 2.9MiB
user> (clojure.contrib.humanize/truncate "abcdefghijklmnopqrstuvwxyz" 10 "...xyz")
=> "abcd...xyz"
user> (clojure.contrib.humanize/oxford ["apple" "orange" "mango" "pear"]
:maximum-display 2
:truncate-noun "fruit")
=> "apple, orange, and 2 other fruits"
user> (clojure.contrib.inflect/pluralize-noun 6 "buzz")
=> "buzzes"
user> (clojure.contrib.humanize/datetime (plus (now) (years -7)))
=> "7 years ago"
user> (clojure.contrib.humanize/duration 325100 {:number-format str})
=> "5 minutes, 25 seconds"
URL (URL Library)
URL is a library that makes working with URLs easier.
=> (url "https://api.twitter.com/")
#cemerick.url.URL{:protocol "https", :username nil, :password nil,
:host "api.twitter.com", :port -1, :path "/", :query nil,
:anchor nil}
=> (url "https://api.twitter.com/" "1" "users" "profile_image" "cemerick")
#cemerick.url.URL{:protocol "https", :username nil, :password nil,
:host "api.twitter.com", :port -1,
:path "/1/users/profile_image/cemerick", :query nil, :anchor nil}
=> (str *1)
"https://api.twitter.com/1/users/profile_image/cemerick"
=> (str (url "https://api.twitter.com/1/users/profile_image/cemerick" "../../lookup.json"))
"https://api.twitter.com/1/users/lookup.json"
Sente (WebSockets)
Sente is a real time web communications library the does bidirectional async comms over WebSockets (AJAX as fallback) working with auto keep-alives, buffering, protocol selection and reconnects.
Slingshot (try/throw)
Slingshot provides try+
and throw+
which is compatible with Java's native exception handling and a power-up to Clojure's native try
and throw
behavior.
throw+
can throw any Java object, not just classes derived fromjava.lang.Throwable
.catch
will catch Java objects thrown bythrow+
, map passedex-info
thrown bythrow
,throw+
or anyThrowable
thrown by Clojure or Java'sthrow
.
(ns math.expression
(:require [tensor.parse]
[clojure.tools.logging :as log])
(:use [slingshot.slingshot :only [throw+ try+]]))
(defn read-file [file]
(try+
[...]
(tensor.parse/parse-tree tree)
[...]
(catch [:type :tensor.parse/bad-tree] {:keys [tree hint]}
(log/error "failed to parse tensor" tree "with hint" hint)
(throw+))
(catch Object _
(log/error (:throwable &throw-context) "unexpected error")
(throw+))))
Timbre (Logging)
Timbre is a logging library for Clojure(Script). It offers full support for v4+ with no XML or properties files. Create a single config map and you're good to go.
Add the dependency to your project and then setup your namespace imports.
(ns my-clj-ns ; Clojure namespace
(:require
[taoensso.timbre :as timbre
:refer [log trace debug info warn error fatal report
logf tracef debugf infof warnf errorf fatalf reportf
spy get-env]]))
(ns my-cljs-ns ; ; ClojureScript namespace
(:require
[taoensso.timbre :as timbre
:refer-macros [log trace debug info warn error fatal report
logf tracef debugf infof warnf errorf fatalf reportf
spy get-env]]))
You can call (timbre/refer-timbre)
to configure the Clj namespace referrals automatically.
Basic println
and js/console
(v4+) output at a :debug
level is given by default.
(info "This will print") => nil
%> 15-Jun-13 19:18:33 localhost INFO [my-app.core] - This will print
(spy :info (* 5 4 3 2 1)) => 120
%> 15-Jun-13 19:19:13 localhost INFO [my-app.core] - (* 5 4 3 2 1) => 120
(defn my-mult [x y] (info "Lexical env:" (get-env)) (* x y)) => #'my-mult
(my-mult 4 7) => 28
%> 15-Jun-13 19:21:53 localhost INFO [my-app.core] - Lexical env: {x 4, y 7}
(trace "This won't print due to insufficient log level") => nil
Other
Shadow CLJS is a ClojureScript development tool.
I haven't used this library. By the looks of it, it compiles ClojureScript code with little configuration. It does this in a specific build order like what you would do in Grunt and Gulp. It has reliable npm integration and some other cool stuff that can be investigated in the guide.
DevCards is a ClojureScript library. You can experiment and inspect your ClojureCode from the REPL-like feature in the browser. This REPL is powered by data in the source files. Code examples can be executed and changes can be inspected directly in the DOM.
Ring is a low-level interface and library for building web applications in the Clojure programming language.
This abstracted HTTP API allows web applications to be constructed of modular components. These components can be shared among applications, web servers and web frameworks.
re-frisk creates a little button on the screen that lets you visualize re-frame pattern data; you can see the reagent ratom as a tree structure; watch re-frame events and export the state in the debugger.
CIDER
CIDER is a Clojure(Script) Interactive Development Environment that Rocks! CIDER extends Emacs with support for interactive programming in Clojure.
Resources
Learn Clojure
- Clojure for the Brave and True by Daniel Higginbotham can be read online for free.
- clojuredocs.org has documentation and examples contributed by the community.
- Clojure TV is a YouTube channel with Clojure talks and presentations. Other video gems are available in YouTube when searching for Clojure.
- The Re-frame Building Blocks Guide
- Learn Reagent Free
Interesting articles
- A Noob's Walkthrough of a re-frame Application
- Why I chose ClojureScript over JavaScript
- 6 mistakes that Reacters do that re-framers avoid