IntelliJ + Cursive

and a bit of Stuart Sierra's reloaded workflow

Karol Andrusieczko

Clojure Meetup, 26.08.2015

Eyeota - they pay for my food

 

Digital advertising industry. Rapidly growing startup.

 

The global leader for local audience data with 1.3 billion unique profiles in Europe, APAC and the Americas.

 

Our partners are:

- advertisers

- publishers

- data suppliers

What's the project about?

WebAPI

Cassandra

ElasticSearch

Postgres (for legacy)

Ring (Immutant)

Compojure

Liberator

Cursive demo

Why Cursive

  • IntelliJ gives the AST
  • You don't have to run the code to get suggestions
  • Flawless navigation
  • Hints
  • Refactoring
  • Compilation errors / warnings live
    • arity warnings
    • unused variables
  • Kibit embedded
  • require/import support, dependency cycles detection

Manipulating syntax, not text

Drawbacks

  • Will be non-free in the future (prices ~PyCharm)
  • Sometimes it doesn't understand protocols/records
  • Sometimes it doesn't understand macros

Advantages

  • Mature IDE / one for all
  • Great intelligence
    • auto-completion
    • navigation
    • refactoring
  • Shortcuts
  • Great REPL
  • Fantastic tests support
  • Debugging
  • RegExp refactoring

Reloaded workflow

by Stuart Sierra

Realoded workflow

Two important things:

clojure.tools.namespace.repl

NO GLOBAL STATE

Realoded workflow

The idea

To be able to shut the application down, discard any transient state it might have built up, start it again, and return to a similar state (...) in less than a second.

Realoded workflow

How?

  • application put into an object
  • you can construct an instance of the application (possibly many times)
  • no global state
  • really, no global state

Realoded workflow

What's a global state?

(def state-a (ref {}))
(def state-b (atom 0))


; hidden dependencies coming from outside

(defn op1 []
  (dosync
    (alter state-a ...)))

(defn op2 []
  (dosync
    (swap! state-b ...)))
(let [state-a (ref {})
      state-b (atom 0)]

      (defn op1 []
        (dosync
          (alter state-a ...)))
    
      (defn op2 []
        (dosync
          (swap! state-b ...))))

Realoded workflow

How to avoid global state?

(defn constructor []
  {:a (ref {})
   :b (atom 0)})


; makes function dependencies clear

(defn op1 [state]
  (dosync
    (alter (:a state) ...)))

(defn op2 [state]
  (dosync
    (swap! (:b state) ...)))

Realoded workflow

Really easy to test!

(defn constructor []
  {:a (ref {:initial "awesome test!"})
   :b (atom 0)})

(deftest test-op1
  (let [state (constructor)]
    (is (= ... (op1 state)))))

and reload! :)

Realoded workflow

Also wrong - global state

(def ^:dynamic *resource*)

(defn- internal-op1 []
  ... *resource* ...)

(defn op1 [arg]
  (internal-op1 ...))

assumptions:

  1. only one resource at the time
  2. working on one thread

Realoded workflow

But... but... what about my db connection?!

(defn init! []
  (connect-to-database!)
  (create-thread-pools!)
  (start-background-processes!)
  (start-web-server!))

Realoded workflow

Constructor

;; In src/com/example/my_project/system.clj

(ns com.example.my-project.system)

(defn system
  "Returns a new instance of the whole application."
  []
  ...)
{:db        {:uri "datomic:mem://dev"}
 :scheduler #<ScheduledThreadPoolExecutorService ...>
 :cache     #<Atom {}>
 :handler   #<Fn ...>
 :server    #<Jetty ...>}

Realoded workflow

Different constructors

(defrecord System
  [storage config web-service])

(defn dev-system []
  (->System (mem-store)
            (local-config {:a 1 :b 2})
            (mock-web-service)))

(defn prod-system []
  (let [config (zookeeper-config)
        storage (sql-store config)
        service (web-srv config storage)]
    (->System storage config service)))

Realoded workflow

start and stop

;; In src/com/example/my_project/system.clj

(defn start
  "Performs side effects to initialize the system, acquire resources,
  and start it running. Returns an updated instance of the system."
  [system]
  ...)

(defn stop
  "Performs side effects to shut down the system and release its
  resources. Returns an updated instance of the system."
  [system]
  ...)

Realoded workflow

dev/user.clj and demo

Links

  • Cursive: https://cursiveclojure.com/eap.html
  • C. Fleming on Cursive: https://www.youtube.com/watch?v=vt1y2FbWQMg
  • Stuart Sierra's reloaded workflow article: http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded
  • Stuart Sierra's talk: http://www.infoq.com/presentations/Clojure-Large-scale-patterns-techniques
Made with Slides.com