Skip to content

Commit

Permalink
Merge pull request #38 from nubank/prune-dependencies
Browse files Browse the repository at this point in the history
Prune dependencies
  • Loading branch information
aredington authored Oct 17, 2024
2 parents 4ddce66 + 9fc2959 commit 16ebe17
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 48 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 2.0.0 / 2024-10-17
- Breaking change: removed transitive dependencies
-- The following dependencies are no longer transitively provided,
but, if otherwise made available on the classpath will continue to be
supported: org.clojure/core.async, funcool/promesa, manifold
-- No code changes are required, but, if using an engine that is
powered by one of these libraries, then clients must include
these libraries on their classpath explicitly, or, transitively
include them through some other library.

## 1.19.1 / 2024-08-28
- Fix Contextual protocol to accept nil context cases

Expand Down
8 changes: 4 additions & 4 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject dev.nu/nodely "1.19.1"
(defproject dev.nu/nodely "2.0.0"
:description "Decoupling data fetching from data dependency declaration"
:url "https://github.com/nubank/nodely"
:license {:name "MIT"}
Expand All @@ -8,9 +8,9 @@

:dependencies [[org.clojure/clojure "1.10.3"]
[aysylu/loom "1.0.2"]
[org.clojure/core.async "1.5.648"]
[funcool/promesa "10.0.594"]
[manifold "0.1.9-alpha5"]
[org.clojure/core.async "1.5.648" :scope "provided"]
[funcool/promesa "10.0.594" :scope "provided"]
[manifold "0.1.9-alpha5" :scope "provided"]
[prismatic/schema "1.1.12"]]

:exclusions [log4j]
Expand Down
131 changes: 93 additions & 38 deletions src/nodely/api/v0.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@
(:require
[nodely.data]
[nodely.engine.applicative :as applicative]
[nodely.engine.applicative.core-async :as applicative.core-async]
[nodely.engine.applicative.promesa :as applicative.promesa]
[nodely.engine.core :as engine-core]
[nodely.engine.core-async.core]
[nodely.engine.core-async.iterative-scheduling]
[nodely.engine.core-async.lazy-scheduling]
[nodely.engine.lazy]
[nodely.engine.manifold]
[nodely.syntax]
[nodely.syntax :as syntax]
[nodely.vendor.potemkin :refer [import-fn import-vars]]))

(import-vars nodely.syntax/>cond
Expand All @@ -22,7 +16,6 @@
nodely.syntax/>value
nodely.syntax/>sequence
nodely.syntax/blocking
nodely.engine.core-async.core/>channel-leaf
nodely.data/value
nodely.data/leaf
nodely.data/sequence
Expand All @@ -33,42 +26,104 @@
(import-fn nodely.data/merge-values merge-values)
(import-fn nodely.data/get-value get-value)

(def virtual-future-failure
(delay
(try (import java.util.concurrent.ThreadPerTaskExecutor)
(require 'nodely.engine.virtual-workers
'nodely.engine.applicative.virtual-future)
(catch Exception e
{:msg "Classloader could not locate `java.util.concurrent.ThreadPerTaskExecutor`, virtual futures require JDK 21 or higher."
::error :missing-class
::requested-class "java.util.concurrent.ThreadPerTaskExecutor"
:cause e}))))

(def core-async-failure
(delay
(try (require 'nodely.engine.applicative.core-async
'nodely.engine.core-async.core
'nodely.engine.core-async.iterative-scheduling
'nodely.engine.core-async.lazy-scheduling)
(catch Exception e
{:msg "Could not locate core-async on classpath."
::error :missing-ns
::requested-namespaces '[nodely.engine.applicative.core-async
nodely.engine.core-async.core
nodely.engine.core-async.iterative-scheduling
nodely.engine.core-async.lazy-scheduling]
:cause e}))))

(def manifold-failure
(delay
(try (require 'nodely.engine.manifold)
(catch Exception e
{:msg "Could not locate manifold on classpath."
::error :missing-ns
::requested-namespaces '[nodely.engine.manifold]
:cause e}))))

(def promesa-failure
(delay
(try (require 'nodely.engine.applicative.promesa)
(catch Exception e
{:msg "Could not locate promesa on classpath."
::error :missing-ns
::requested-namespaces '[nodely.engine.applicative.promesa]
:cause e}))))

(def engine-data
{:core-async.lazy-scheduling {::ns (find-ns 'nodely.engine.core-async.lazy-scheduling)
::opts-fn identity
::eval-key-channel true}
:core-async.iterative-scheduling {::ns (find-ns 'nodely.engine.core-async.iterative-scheduling)
::opts-fn identity}
:async.manifold {::ns (find-ns 'nodely.engine.manifold)
::opts-fn (constantly nil)}
:applicative.promesa {::ns (find-ns 'nodely.engine.applicative)
::opts-fn #(assoc % ::applicative/context applicative.promesa/context)}
:applicative.core-async {::ns (find-ns 'nodely.engine.applicative)
::opts-fn #(assoc % ::applicative/context applicative.core-async/context)
{:core-async.lazy-scheduling {::ns-name 'nodely.engine.core-async.lazy-scheduling
::opts-fn identity
::enable-deref core-async-failure
::eval-key-channel true}
:sync.lazy {::ns (find-ns 'nodely.engine.lazy)
::opts-fn (constantly nil)
::eval-key-channel true}})

;; Java 21 Virtual Threads Support
(try (import java.util.concurrent.ThreadPerTaskExecutor)
(require '[nodely.engine.virtual-workers])
(require '[nodely.engine.applicative.virtual-future :as applicative.virtual-future])
(alter-var-root #'engine-data assoc
:async.virtual-futures {::ns (find-ns 'nodely.engine.virtual-workers)
::opts-fn (constantly nil)
::eval-key-channel true}
:applicative.virtual-future {::ns (find-ns 'nodely.engine.applicative)
::opts-fn #(assoc % ::applicative/context
(var-get (resolve 'nodely.engine.applicative.virtual-future/context)))
::eval-key-channel true})
(catch Exception e e))
;; End Virtual Threads
:core-async.iterative-scheduling {::ns-name 'nodely.engine.core-async.iterative-scheduling
::opts-fn identity
::enable-deref core-async-failure}
:async.manifold {::ns-name 'nodely.engine.manifold
::opts-fn (constantly nil)
::enable-deref manifold-failure}
:applicative.promesa {::ns-name 'nodely.engine.applicative
::opts-fn #(assoc % ::applicative/context
(var-get (resolve 'nodely.engine.applicative.promesa/context)))
::enable-deref promesa-failure}
:applicative.core-async {::ns-name 'nodely.engine.applicative
::opts-fn #(assoc % ::applicative/context
(var-get (resolve 'nodely.engine.applicative.core-async/context)))
::eval-key-channel true
::enable-deref core-async-failure}
:sync.lazy {::ns-name 'nodely.engine.lazy
::opts-fn (constantly nil)
::eval-key-channel true
::enable-deref (delay nil)}
:async.virtual-futures {::ns-name 'nodely.engine.virtual-workers
::opts-fn (constantly nil)
::eval-key-channel true
::enable-deref virtual-future-failure}
:applicative.virtual-future {::ns-name 'nodely.engine.applicative
::opts-fn #(assoc % ::applicative/context
(var-get (resolve 'nodely.engine.applicative.virtual-future/context)))
::eval-key-channel true
::enable-deref virtual-future-failure}})

(defmacro >channel-leaf
[expr]
(let [symbols-to-be-replaced (#'syntax/question-mark-symbols expr)
fn-expr (#'syntax/fn-with-arg-map symbols-to-be-replaced expr)]
(if-let [{:keys [msg cause] :as enable-failure} @core-async-failure]
(throw (ex-info msg (dissoc enable-failure :msg :cause) cause))
(list `nodely.engine.core-async.core/channel-leaf
(mapv #'syntax/question-mark->keyword symbols-to-be-replaced)
fn-expr))))

(defn- engine-fn
[engine-name use]
(if-let [engine-data (engine-data engine-name)]
(ns-resolve (::ns engine-data) use)
(if-let [{:keys [msg cause] :as enable-failure} @(::enable-deref engine-data)]
(throw (ex-info msg
(-> enable-failure
(dissoc :msg :cause)
(assoc ::specified-engine-name engine-name))
cause))
(ns-resolve (find-ns (::ns-name engine-data)) use))
(throw (ex-info "Unsupported engine specified, please specify a supported engine."
{:specified-engine-name engine-name
:supported-engine-names (set (keys engine-data))}))))
Expand Down
106 changes: 100 additions & 6 deletions test/nodely/api_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
:i (blocking (>leaf (do (Thread/sleep 1000) :i)))
:z (>leaf (into #{} [?a ?b ?c ?d ?e ?f ?g ?h ?i]))})

(def env+channel-leaf {:a (>value 1)
:b (api/>channel-leaf
(async/go (+ ?a 5)))
:c (>leaf (+ ?a ?b))})

(def parallel-engines
#{:core-async.lazy-scheduling
:core-async.iterative-scheduling
Expand All @@ -54,10 +59,92 @@
[engine-key]
(get-in api/engine-data [engine-key ::api/eval-key-channel]))

(defn ensure-unrealized-delay
[sym]
(when (realized? (deref (resolve sym)))
(require :reload '[nodely.api.v0])))

(defn- testing-require-delay-call
[ns-sym delay message cause call-fn]
(do (ensure-unrealized-delay delay)
(let [orig-require require
bomb (fn [& args]
(if ((set args) ns-sym)
(throw (ex-info message
{:cause cause}))
(apply orig-require args)))
res (with-redefs [require bomb]
(call-fn))]
(require :reload 'nodely.api.v0)
res)))

(defmacro testing-require-delay
[ns-sym delay message cause & body]
`(testing-require-delay-call (quote ~ns-sym) (quote ~delay)
~message ~cause (^{:once true} fn* [] ~@body)))

(t/deftest engine-without-support
(t/testing "engines blowing up"
(testing-require-delay
nodely.engine.virtual-workers nodely.api.v0/virtual-future-failure
"Kaboom! We're not on JVM 21 for pretend" :test-virtual-future-failure
(t/testing "without virtual futures in the JVM"
(t/testing "attempting to use virtual futures"
(t/matching
#"Classloader could not locate `java.util.concurrent.ThreadPerTaskExecutor`"
(try (api/eval-key-channel env :z {::api/engine :applicative.virtual-future})
(catch Throwable t
(ex-message t)))))
(t/testing "attempting to use core.async"
(t/matching
5
(async/<!! (api/eval-key-channel env :z {::api/engine :core-async.lazy-scheduling}))))))
(testing-require-delay
nodely.engine.core-async.core nodely.api.v0/core-async-failure
"Kaboom! We don't have core.async for pretend" :test-core-async-failure
(t/testing "without core.async on the classpath"
(t/testing "attempting to use core.async"
(t/matching
#"Could not locate core-async on classpath"
(try (api/eval-key-channel env :z {::api/engine :core-async.lazy-scheduling})
(catch Throwable t
(ex-message t)))))
(t/testing "attempting to use manifold"
(t/matching
5
(api/eval-key env :z {::api/engine :async.manifold})))))
(testing-require-delay
nodely.engine.manifold nodely.api.v0/manifold-failure
"Kaboom! We don't have manifold for pretend" :test-manifold-failure
(t/testing "without manifold on the classpath"
(t/testing "attempting to use manifold"
(t/matching
#"Could not locate manifold on classpath"
(try (api/eval-key-channel env :z {::api/engine :async.manifold})
(catch Throwable t
(ex-message t)))))
(t/testing "attempting to use core.async"
(t/matching
5
(async/<!! (api/eval-key-channel env :z {::api/engine :core-async.lazy-scheduling}))))))
(testing-require-delay
nodely.engine.applicative.promesa nodely.api.v0/promesa-failure
"Kaboom! We don't have promesa for pretend" :test-promesa-failure
(t/testing "attempting to use promesa without promesa on the classpath"
(t/testing "attempting to use promesa"
(t/matching
#"Could not locate promesa on classpath"
(try (api/eval-key-channel env :z {::api/engine :applicative.promesa})
(catch Throwable t
(ex-message t)))))
(t/testing "attempting to use core.async"
(t/matching
5
(async/<!! (api/eval-key-channel env :z {::api/engine :core-async.lazy-scheduling}))))))))

(defn engine-test-suite
[engine-key]
(t/testing (name engine-key)

(when (channel-interface engine-key)
(t/testing "eval-*-channel-test"
(t/testing "returning a result to a channel with each engine"
Expand Down Expand Up @@ -102,13 +189,20 @@
(t/testing "handling nested exceptions"
(t/matching #"Oops!"
(try (api/eval-key exceptions-all-the-way-down :d {::api/engine engine-key})
(catch clojure.lang.ExceptionInfo e (ex-message e)))))))
(catch clojure.lang.ExceptionInfo e (ex-message e)))))

(t/testing "env with >channel-leaf"
(t/matching 7 (api/eval-key env+channel-leaf :c {::api/engine engine-key})))))

(t/deftest api-test
(for [engine (set/difference (set (keys api/engine-data))
#{:core-async.iterative-scheduling
:async.virtual-futures})]
(engine-test-suite engine)))
(let [remove-keys (conj #{:core-async.iterative-scheduling
:async.virtual-futures}
(when (try (import java.util.concurrent.ThreadPerTaskExecutor)
(catch Throwable t t))
:applicative.virtual-future))]
(for [engine (set/difference (set (keys api/engine-data))
remove-keys)]
(engine-test-suite engine))))

(t/deftest incorrect-engine-id
(t/testing "we communicate how the client has specified an invalid input and what would be valid inputs"
Expand Down

0 comments on commit 16ebe17

Please sign in to comment.