Optimizing your experience
Gradient background

Clojure Buzzwords I am Learning

Clarice Bouwer

Software Engineering Team Lead and Director of Cloudsure

Wednesday, 15 August 2018 · Estimated 8 minute read

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)

CircleCI License 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.

Copy
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
Copy
{:db/id 42
 :user/favorite-color :crimson
 :user/first-name "Douglas"
 :user/last-name "Adams"}

Reagent (React framework)

CircleCI License: MIT 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.

Copy
(defn home-panel []
  [:div.hello-world
    [:p
      [:b "Hello World"]]])
Copy
<div data-reactroot="" class="hello-world">
  <p><b>Hello World</b></p>
</div>

re-frame (React pattern)

Circle CI License: MIT 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.

"The 6 Dominoes"

Figwheel (Hot reloader)

CircleCI License 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.

Copy
lein new figwheel hello-world -- --reagent  #for a reagent based project

Garden (ClojureScript to CSS)

License 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.

Copy
(require '[garden.core :refer [css]])
(css [:body {:font-size "16px"}])
Copy
body {
  font-size: 16px;
}

Compojure (Server-side routing)

Build Status License Compojure is a small server-side routing library for Ring that allows web applications to be composed of small, independent parts.

Copy
lein new compojure hello-world
cd hello-world
lein ring server-headless
Copy
(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)

License Secretary is a client-side router for ClojureScript. It is built to create route matchers and dispatch actions.

Copy
(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)

Build Status License 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:

Copy
(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)

Build Status License: MIT Bouncer is a validation DSL.

Copy
(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)

Build Status License 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.

Copy
(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)

Build Status License Humanize produces human readable strings and dates.

Copy
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)

Travis CI status License URL is a library that makes working with URLs easier.

Copy
=> (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)

License 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)

Build Status License 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 from java.lang.Throwable.
  • catch will catch Java objects thrown by throw+, map passed ex-info thrown by throw, throw+ or any Throwable thrown by Clojure or Java's throw.
Copy
(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)

License 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.

Copy

(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.

Copy
(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

License 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

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

Build Status License: MIT 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

License: MIT 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.

"re-frisk panel from flexsurfer/re-frisk on GitHub"

CIDER

License: GPL v3 CIDER is a Clojure(Script) Interactive Development Environment that Rocks! CIDER extends Emacs with support for interactive programming in Clojure.

Resources

Learn Clojure

Interesting articles