Skip to content

fl00r/flow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

flow

Graph oriented programming.

An alternative way of writing code rich of conditionals.

Instead of

(defn sell-booze
  [resources data]
  (let [profile (get-profile-data resources data)]
    (if (got-the-id? profile)
      (if (is-under-21? profile)
        (dont-sell)
        (let [supply (get-supply resources data)]
          (if (is-enough-supply? supply)
            (if (is-enough-money data)
              (sell)
              (not-enough-money))
            (not-enough-supply))))
      (dont-sell))))

we do

(def sell-booze-graph
  {get-profile-data {got-the-id? :check-age
                     flow/otherwise dont-sell}
   :check-age {is-under-21? dont-sell
               flow/otherwise get-supply}
   get-supply {is-enough-supply? :check-money
               flow/otherwise inform-not-enough-supply}
   :check-money {is-enough-money? sell
                 flow/otherwise inform-not-enough-money}
   inform-not-enough-supply inform-available-amount-of-supply
   inform-not-enough-money inform-affordable-amount-of-booze})

Usage

(require '[flow.core :as flow])

(defn- get-profile-data
  [resources data]
  (let [{:keys [id]} data
        profile (get-in resources [:profiles id])]
    (assoc data :profile profile)))

(defn- get-supply
  [resources data]
  (let [{:keys [booze]} data
        {:keys [supplies]} resources
        supply (get supplies booze)]
    (assoc data :supply supply)))

(defn- inform-not-enough-supply
  [resources data]
  (assoc data :result {:status :not-enough-booze}))

(defn- inform-not-enough-money
  [resources data]
  (assoc data :result {:status :not-enough-money}))

(defn- inform-available-amount-of-supply
  [resources data]
  (let [real-amount (get-in data [:supply :amount] 0)]
    (assoc-in data [:result :available] real-amount)))

(defn- inform-affordable-amount-of-booze
  [resources data]
  (let [{:keys [money supply]} data
        {:keys [price]} supply
        affordable-amount (int (/ money price))]
    (assoc-in data [:result :affordable] affordable-amount)))

(defn- dont-sell
  [resources data]
  (assoc data :result {:status :can-not-sell}))

(defn- sell
  [resources data]
  (assoc data :result {:status :here-you-are}))

(defn- got-the-id?
  [resources data]
  (let [{:keys [profile]} data]
    (not (nil? profile))))

(defn- is-under-21?
  [resources data]
  (let [{:keys [profile]} data
        {:keys [age]} profile]
    (< age 21)))

(defn- is-enough-supply?
  [resources data]
  (let [{:keys [amount supply]} data
        {real-amount :amount} supply]
    (and real-amount
         (>= real-amount amount))))

(defn- is-enough-money?
  [resources data]
  (let [{:keys [amount money supply]} data
        {real-amount :amount
         :keys [price]} supply
        total-price (* amount price)]
    (>= money total-price)))

(def sell-booze-graph
  {get-profile-data {got-the-id? :check-age
                     flow/otherwise dont-sell}
   :check-age {is-under-21? dont-sell
               flow/otherwise get-supply}
   get-supply {is-enough-supply? :check-money
               flow/otherwise inform-not-enough-supply}
   :check-money {is-enough-money? sell
                 flow/otherwise inform-not-enough-money}
   inform-not-enough-supply inform-available-amount-of-supply
   inform-not-enough-money inform-affordable-amount-of-booze})

(deftest get-the-booze-flow
  (let [resources {:supplies {"Beer" {:amount 10
                                      :price 7}
                              "Wine" {:amount 5
                                      :price 12}
                              "Whiskey" {:amount 2
                                         :price 22}
                              "Gin" {:amount 3
                                     :price 17}}
                   :profiles {"Ivan" {:age 30}
                              "Andrew" {:age 30}
                              "Eva" {:age 2}}}]
    (testing "all good"
      (let [data {:id "Ivan"
                  :money 100
                  :booze "Beer"
                  :amount 3}
            {:keys [result]} (flow/execute {:graph sell-booze-graph
                                            :start get-profile-data}
                                           resources
                                           data)]
        (is (= {:status :here-you-are}
               result))))
    (testing "not enough booze"
      (let [data {:id "Ivan"
                  :money 100
                  :booze "Wine"
                  :amount 10}
            {:keys [result]} (flow/execute {:graph sell-booze-graph
                                            :start get-profile-data}
                                           resources
                                           data)]
        (is (= {:status :not-enough-booze
                :available 5}
               result))))
    (testing "not enough money"
      (let [data {:id "Andrew"
                  :money 20
                  :booze "Beer"
                  :amount 5}
            {:keys [result]} (flow/execute {:graph sell-booze-graph
                                            :start get-profile-data}
                                           resources
                                           data)]
        (is (= {:status :not-enough-money
                :affordable 2}
               result))))
    (testing "no ID"
      (let [data {:id "Ira"
                  :money 1000
                  :booze "Whiskey"
                  :amount 30}
            {:keys [result]} (flow/execute {:graph sell-booze-graph
                                            :start get-profile-data}
                                           resources
                                           data)]
        (is (= {:status :can-not-sell}
               result))))
    (testing "too young"
      (let [data {:id "Eva"
                  :money 50
                  :booze "Whiskey"
                  :amount 1}
            {:keys [result]} (flow/execute {:graph sell-booze-graph
                                            :start get-profile-data}
                                           resources
                                           data)]
        (is (= {:status :can-not-sell}
               result))))))

About

graph oriented programming

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published