Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dependency management in Clojure -- long version

schaueho
February 20, 2016

Dependency management in Clojure -- long version

A longer version of the presentation about dependency management in Clojure that I gave at :clojureD 2016, Berlin, which has most of the comments on separate slides and adds discussion of mount.

schaueho

February 20, 2016
Tweet

More Decks by schaueho

Other Decks in Programming

Transcript

  1. View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. defn
    :refer
    :refer :all

    View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. (ns playitloud.ui-player
    (:require [playitloud.simple-player :refer [play]]))
    (defn play-pressed [randomize]
    (println "User pressed play")
    (play randomize))

    View Slide

  15. (ns playitloud.ui-player
    (:require [playitloud.simple-player :refer [play]]))
    (defn play-pressed [randomize]
    (println "User pressed play")
    (play randomize))
    (ns playitloud.simple-player
    (:require [playitloud.speaker :refer [blare]]
    [playitloud.musiccoll :refer [get-songs]]))
    (defn play [randomize]
    (let [songs (get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blare song))
    songs)))

    View Slide

  16. (ns playitloud.ui-player
    (:require [playitloud.simple-player :refer [play]]))
    (defn play-pressed [randomize]
    (println "User pressed play")
    (play randomize))
    (ns playitloud.simple-player
    (:require [playitloud.speaker :refer [blare]]
    [playitloud.musiccoll :refer [get-songs]]))
    (defn play [randomize]
    (let [songs (get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blare song))
    songs)))
    (ns playitloud.speaker)
    (defn blare [song]
    (let [result (str "Speaker plays " song)]
    (println result)
    result))

    View Slide

  17. speaker
    musiccoll

    View Slide

  18. View Slide

  19. (ns playitloud.ho.player)
    (defn play [blarefn songgetterfn randomize]
    (let [songs (songgetterfn)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blarefn song))
    songs)))

    View Slide

  20. (ns playitloud.ho.player)
    (defn play [blarefn songgetterfn randomize]
    (let [songs (songgetterfn)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blarefn song))
    songs)))
    ; ---------------------------------------------------
    (ns playitloud.ho.ui-player
    (:require [playitloud.ho.player :as ho :refer [play]]
    [playitloud.speaker :as speaker]
    [playitloud.headphone :as headphone]
    [playitloud.blue-streamer :as streamer]
    [playitloud.musiccoll :refer [get-songs]]))
    (defn play-pressed [speaker randomize]
    (println "User pressed play")
    (condp = speaker
    :speaker (play speaker/blare get-songs randomize)
    :headphone (play headphone/blare get-songs randomize)
    :stream (play streamer/blare get-songs randomize)))

    View Slide

  21. View Slide

  22. (defn make-playfn
    "Returns a function that will play all songs"
    [blarefn songgetterfn]
    (fn ; Plays all songs, potentially randomized
    [randomize]
    (let [songs (songgetterfn)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blarefn song))
    songs))))

    View Slide

  23. View Slide

  24. (ns playitloud.ho.play-config [...]
    (def speaker-play (make-playfn speaker/blare get-songs))
    (def headphone-play (make-playfn headphone/blare get-songs))
    (def stream-play (make-playfn streamer/blare get-songs))
    (defn select-playfn [speaker]
    (condp = speaker
    :speaker speaker-play
    :headphone headphone-play
    :stream stream-play))
    (ns playitloud.ho.clos-ui-player
    (:require [playitloud.ho.play-config :refer [select-playfn]]))
    (defn play-pressed [output randomize]
    (let [playfn (select-playfn output)]
    (playfn randomize)))

    View Slide

  25. View Slide

  26. (ns playitloud.services.config)
    (def ^:dynamic *services*
    {:blare playitloud.speaker/blare
    :get-songs playitloud.musiccoll/get-songs})

    View Slide

  27. (ns playitloud.services.config)
    (def ^:dynamic *services*
    {:blare playitloud.speaker/blare
    :get-songs playitloud.musiccoll/get-songs})
    (ns playitloud.services.player
    (:require [playitloud.services.config :refer [*services*]]))
    (defn- blare [sound]
    ((:blare *services*) sound))
    (defn- get-songs []
    ((:get-songs *services*)))

    View Slide

  28. (ns playitloud.services.config)
    (def ^:dynamic *services*
    {:blare playitloud.speaker/blare
    :get-songs playitloud.musiccoll/get-songs})
    (ns playitloud.services.player
    (:require [playitloud.services.config :refer [*services*]]))
    (defn- blare [sound]
    ((:blare *services*) sound))
    (defn- get-songs []
    ((:get-songs *services*)))
    (defn play [randomize]
    (let [songs (get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blare song))
    songs)))

    View Slide

  29. *services*

    View Slide

  30. (ns playitloud.sig.output-device
    (:require [de.find-method.funsig :refer [defsig]]))
    (defsig blare "Play sound loudly!" [sound])

    View Slide

  31. (ns playitloud.sig.output-device
    (:require [de.find-method.funsig :refer [defsig]]))
    (defsig blare "Play sound loudly!" [sound])
    (ns playitloud.sig.speaker
    (:require [de.find-method.funsig :refer [defimpl]]
    [playitloud.sig.output-device :refer [blare]]))
    (defimpl blare [sound]
    (let [result (str "Speaker plays " sound)]
    (println result)
    result))

    View Slide

  32. (ns playitloud.sig.output-device
    (:require [de.find-method.funsig :refer [defsig]]))
    (defsig blare "Play sound loudly!" [sound])
    (ns playitloud.sig.speaker
    (:require [de.find-method.funsig :refer [defimpl]]
    [playitloud.sig.output-device :refer [blare]]))
    (defimpl blare [sound]
    (let [result (str "Speaker plays " sound)]
    (println result)
    result))
    (ns playitloud.sig.player
    (:require [playitloud.sig.output-device :refer [blare]]
    [playitloud.musiccoll :as mc :refer [get-songs]]))
    (defn play [randomize]
    (let [songs (get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blare song))
    songs)))

    View Slide

  33. View Slide

  34. (ns playitloud.di.config
    (:require [clj-di.core :refer [register!]]
    [playitloud.speaker :as speaker :refer [blare]]))
    (register! :blare blare)
    ; ---------------------------------------------------
    (ns playitloud.di.player
    (:require [clj-di.core :refer [get-dep]]
    [playitloud.musiccoll :refer [get-songs]]))
    (defn play [randomize]
    (let [blarefn (get-dep :blare) ; <-- get dependency
    songs (get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blarefn song))
    songs)))

    View Slide

  35. with-registered

    View Slide

  36. (ns playitloud.proto.output-device)
    (defprotocol OutputDevice
    (blare [device sound])
    (inc-volume [device])
    (dec-volume [device]))
    (ns playitloud.proto.speaker
    (:require [playitloud.proto.output-device
    :as output :refer :all]))
    (defrecord Speaker [volume]
    OutputDevice
    (blare [_ sound]
    (let [result (str "Speaker plays " sound)]
    (println result)
    result))
    ;[...]

    View Slide

  37. (ns playitloud.proto.player
    (:require [playitloud.proto.output-device :as output]
    [playitloud.musiccoll :as mc]))
    (defn play [output-device randomize]
    (let [songs (mc/get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (output/blare output-device song))
    songs)))

    View Slide

  38. View Slide

  39. View Slide

  40. View Slide

  41. (ns playitloud.multi.output-device)
    (defmulti blare
    "Play sounds on an output device"
    (fn [device sound]
    device))

    View Slide

  42. (ns playitloud.multi.output-device)
    (defmulti blare
    "Play sounds on an output device"
    (fn [device sound]
    device))
    (ns playitloud.multi.speaker
    (:require [playitloud.multi.output-device :refer [blare]]))
    (defmethod blare :speaker [device song]
    (let [result (str "Speaker plays " song)]
    (println result)
    result))

    View Slide

  43. (ns playitloud.multi.output-device)
    (defmulti blare
    "Play sounds on an output device"
    (fn [device sound]
    device))
    (ns playitloud.multi.speaker
    (:require [playitloud.multi.output-device :refer [blare]]))
    (defmethod blare :speaker [device song]
    (let [result (str "Speaker plays " song)]
    (println result)
    result))
    (ns playitloud.multi.headphone
    (:require [playitloud.multi.output-device :refer [blare]]))
    (defmethod blare :headphone [device song]
    (let [result (str "Headphone plays " song)]
    (println result)
    result))

    View Slide

  44. (ns playitloud.multi.player
    (:require [playitloud.multi.output-device :refer [blare]]
    [playitloud.musiccoll :as mc]))
    (defn play [output-device randomize]
    (let [songs (mc/get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blare output-device song))
    songs)))
    (ns playitloud.multi.ui
    (:require [playitloud.multi.player :as player :refer [play]]))
    (defn play-pressed [output-selection randomize]
    (println "User pressed play")
    (play output-selection randomize))

    View Slide

  45. defmulti

    View Slide

  46. View Slide

  47. View Slide

  48. View Slide

  49. (ns playitloud.closure-streamer)
    (defn blare [connection-config]
    (let [connection (connect connection-config " closure streamer")]
    (fn [noise]
    (let [result (str "Streaming " noise " to " connection)]
    (println result)
    (transmit connection noise)
    result))))

    View Slide

  50. View Slide

  51. (ns playitloud.dynvar.config)
    (def ^:dynamic *streamer*
    {:connection ; [...]})
    (ns playitloud.dynvar.streamer
    (:require [playitloud.dynvar.config :as config]))
    (defn blare [noise]
    (let [result (str "Streaming " noise " to " config/*connection*)]
    (transmit (:connection config/*streamer* noise)
    result)))

    View Slide

  52. with-...
    binding map

    View Slide

  53. View Slide

  54. (ns playitloud.comp.streamer
    (:require [com.stuartsierra.component :as component]
    [playitloud.comp.output-device :as output :refer :all]
    [playitloud.comp.remote-connection :as remote]))
    (defrecord BlueStreamer [connection volume]
    OutputDevice
    (blare [streamer sound]
    ;... normal protocol/record implementation ...)
    component/Lifecycle
    (start [streamer]
    (println "BlueStreamer starting")
    (connect (:connection streamer) streamer)
    (assoc streamer :status :connected))
    (stop [streamer]
    (println "BlueStreamer stopping")
    (disconnect (:connection streamer) streamer)
    (->> (assoc streamer :status :disconnected)
    (dissoc :connection))))
    (defn new-blue-streamer [config]
    (->BlueStreamer nil (:default-volume config)))

    View Slide

  55. (ns playitloud.comp.player
    (:require [com.stuartsierra.component :as component]
    [playitloud.comp.output-device :as output]
    [playitloud.musiccoll :as mc]))
    (defrecord Player [output-device]
    component/Lifecycle
    (start [player]
    (println "Player started with "
    "output-device " (:output-device player))
    player)
    (stop [player]
    (println "Player stopped")
    player))
    (defn play [player randomize]
    (let [songs (mc/get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (output/blare (:output-device player) song))
    songs)))
    (defn new-player []
    (->Player nil))

    View Slide

  56. (ns playitloud.comp.config
    (:require [com.stuartsierra.component :as component]
    [playitloud.comp.blue-connection :as blueconn]
    [playitloud.comp.streamer :as streamer]
    [playitloud.comp.player :as player]))
    (def default-config
    {:bt-conn {:name "Fabuluous-Connectivity" :port "1234"}
    :default-volume 5})
    (defn make-player-system
    "Setup the player system"
    ([]
    (make-player-system default-config))
    ([config]
    (component/system-map
    :connection (blueconn/new-connection config)
    :output-device (component/using
    (streamer/new-blue-streamer config)
    [:connection])
    :player (component/using
    (player/new-player)
    [:output-device]))))

    View Slide

  57. fun-a
    A
    fun-b fun-a
    A
    B
    B A

    View Slide

  58. View Slide

  59. :require :refer

    View Slide

  60. (ns playitloud.mount.streamer
    (:require [mount.core :refer [defstate]]
    [playitloud.mount.connection
    :refer [conn transmit send-command]]))
    (defn create-streamer [connection]
    (println "Connecting to connection ..." connection)
    {:connection connection})
    (defstate streamer :start (create-streamer conn))
    (defn blare [streamer sound]
    (let [result (str "Will stream " sound
    " to " (:connection streamer))]
    (println result)
    (transmit conn sound)
    result))

    View Slide

  61. (ns playitloud.mount.player
    (:require [mount.core :refer [defstate]]
    [playitloud.mount.streamer :refer [streamer blare]]
    [playitloud.musiccoll :as mc]))
    (defn create-player [streamer]
    (println "Will use streamer " streamer)
    {:streamer streamer})
    (defstate player :start (create-player streamer))
    (defn play [randomize]
    (let [songs (mc/get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blare (:streamer player) song))
    songs)))

    View Slide

  62. (ns playitloud.mount.player
    (:require [mount.core :refer [defstate]]
    [playitloud.mount.streamer :refer [streamer blare]]
    [playitloud.musiccoll :as mc]))
    (defn create-player [streamer]
    (println "Will use streamer " streamer)
    {:streamer streamer})
    (defstate player :start (create-player streamer))
    (defn play [randomize]
    (let [songs (mc/get-songs)
    songs (if randomize (shuffle songs) songs)]
    (map (fn [song]
    (blare (:streamer player) song))
    songs)))
    (facts "Handling state with mount"
    (with-state-changes [(before :facts (mount/start))
    (after :facts (mount/stop))]
    (fact "We can play over the configured device"
    (play false)
    => (just ["Will stream David Bowie -- Blackstar " ; ...

    View Slide

  63. DeferableState
    IDeref
    :started

    View Slide

  64. View Slide

  65. View Slide

  66. View Slide

  67. View Slide

  68. View Slide