├── .gitignore
├── README.md
├── dev
├── example.clj
└── user.clj
├── epl-v10.html
├── project.clj
├── src
└── com
│ └── redbrainlabs
│ ├── system_graph.clj
│ └── system_graph
│ └── utils.clj
└── test
└── com
└── redbrainlabs
├── system_graph
└── utils_test.clj
└── system_graph_test.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | pom.xml.asc
7 | *.jar
8 | *.class
9 | .lein-deps-sum
10 | .lein-failures
11 | .lein-plugins
12 | .lein-repl-history
13 | .nrepl-port
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # system-graph
2 |
3 | 'system-graph' is a Clojure library for using Prismatic's [Graph] in large system composition.
4 |
5 | 'Graph' provides a form of dependency injection which does all of the hard work. 'system-graph'
6 | builds on top of this to allow Graphs to be compiled so that `SystemGraph`s are returned. These
7 | `SystemGraph`s implement a `Lifecycle` protocol that enables the components of the system to be
8 | started and shut down in a coordinated fashion. The beauty of using 'Graph' is that [the correct
9 | order] of the components is implicitly defined by the Graph's [Fnks].
10 |
11 | [Graph]: https://github.com/Prismatic/plumbing#graph-the-functional-swiss-army-knife
12 | [Fnks]: https://github.com/Prismatic/plumbing#bring-on-defnk
13 | [the correct order]: http://en.wikipedia.org/wiki/Topological_sorting
14 |
15 | ## Releases and Dependency Information
16 |
17 | * Releases are published to [Clojars]
18 |
19 | * Latest stable release is [0.3.0](https://clojars.org/com.redbrainlabs/system-graph/versions/0.3.0)
20 |
21 | * [All releases](https://clojars.org/com.redbrainlabs/system-graph/versions)
22 |
23 | [Leiningen] dependency information:
24 |
25 | [com.redbrainlabs/system-graph "0.3.0"]
26 |
27 | [Maven] dependency information:
28 |
29 |
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 31 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR 32 | DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS 33 | AGREEMENT.
34 | 35 |1. DEFINITIONS
36 | 37 |"Contribution" means:
38 | 39 |a) in the case of the initial Contributor, the initial 40 | code and documentation distributed under this Agreement, and
41 |b) in the case of each subsequent Contributor:
42 |i) changes to the Program, and
43 |ii) additions to the Program;
44 |where such changes and/or additions to the Program 45 | originate from and are distributed by that particular Contributor. A 46 | Contribution 'originates' from a Contributor if it was added to the 47 | Program by such Contributor itself or anyone acting on such 48 | Contributor's behalf. Contributions do not include additions to the 49 | Program which: (i) are separate modules of software distributed in 50 | conjunction with the Program under their own license agreement, and (ii) 51 | are not derivative works of the Program.
52 | 53 |"Contributor" means any person or entity that distributes 54 | the Program.
55 | 56 |"Licensed Patents" mean patent claims licensable by a 57 | Contributor which are necessarily infringed by the use or sale of its 58 | Contribution alone or when combined with the Program.
59 | 60 |"Program" means the Contributions distributed in accordance 61 | with this Agreement.
62 | 63 |"Recipient" means anyone who receives the Program under 64 | this Agreement, including all Contributors.
65 | 66 |2. GRANT OF RIGHTS
67 | 68 |a) Subject to the terms of this Agreement, each 69 | Contributor hereby grants Recipient a non-exclusive, worldwide, 70 | royalty-free copyright license to reproduce, prepare derivative works 71 | of, publicly display, publicly perform, distribute and sublicense the 72 | Contribution of such Contributor, if any, and such derivative works, in 73 | source code and object code form.
74 | 75 |b) Subject to the terms of this Agreement, each 76 | Contributor hereby grants Recipient a non-exclusive, worldwide, 77 | royalty-free patent license under Licensed Patents to make, use, sell, 78 | offer to sell, import and otherwise transfer the Contribution of such 79 | Contributor, if any, in source code and object code form. This patent 80 | license shall apply to the combination of the Contribution and the 81 | Program if, at the time the Contribution is added by the Contributor, 82 | such addition of the Contribution causes such combination to be covered 83 | by the Licensed Patents. The patent license shall not apply to any other 84 | combinations which include the Contribution. No hardware per se is 85 | licensed hereunder.
86 | 87 |c) Recipient understands that although each Contributor 88 | grants the licenses to its Contributions set forth herein, no assurances 89 | are provided by any Contributor that the Program does not infringe the 90 | patent or other intellectual property rights of any other entity. Each 91 | Contributor disclaims any liability to Recipient for claims brought by 92 | any other entity based on infringement of intellectual property rights 93 | or otherwise. As a condition to exercising the rights and licenses 94 | granted hereunder, each Recipient hereby assumes sole responsibility to 95 | secure any other intellectual property rights needed, if any. For 96 | example, if a third party patent license is required to allow Recipient 97 | to distribute the Program, it is Recipient's responsibility to acquire 98 | that license before distributing the Program.
99 | 100 |d) Each Contributor represents that to its knowledge it 101 | has sufficient copyright rights in its Contribution, if any, to grant 102 | the copyright license set forth in this Agreement.
103 | 104 |3. REQUIREMENTS
105 | 106 |A Contributor may choose to distribute the Program in object code 107 | form under its own license agreement, provided that:
108 | 109 |a) it complies with the terms and conditions of this 110 | Agreement; and
111 | 112 |b) its license agreement:
113 | 114 |i) effectively disclaims on behalf of all Contributors 115 | all warranties and conditions, express and implied, including warranties 116 | or conditions of title and non-infringement, and implied warranties or 117 | conditions of merchantability and fitness for a particular purpose;
118 | 119 |ii) effectively excludes on behalf of all Contributors 120 | all liability for damages, including direct, indirect, special, 121 | incidental and consequential damages, such as lost profits;
122 | 123 |iii) states that any provisions which differ from this 124 | Agreement are offered by that Contributor alone and not by any other 125 | party; and
126 | 127 |iv) states that source code for the Program is available 128 | from such Contributor, and informs licensees how to obtain it in a 129 | reasonable manner on or through a medium customarily used for software 130 | exchange.
131 | 132 |When the Program is made available in source code form:
133 | 134 |a) it must be made available under this Agreement; and
135 | 136 |b) a copy of this Agreement must be included with each 137 | copy of the Program.
138 | 139 |Contributors may not remove or alter any copyright notices contained 140 | within the Program.
141 | 142 |Each Contributor must identify itself as the originator of its 143 | Contribution, if any, in a manner that reasonably allows subsequent 144 | Recipients to identify the originator of the Contribution.
145 | 146 |4. COMMERCIAL DISTRIBUTION
147 | 148 |Commercial distributors of software may accept certain 149 | responsibilities with respect to end users, business partners and the 150 | like. While this license is intended to facilitate the commercial use of 151 | the Program, the Contributor who includes the Program in a commercial 152 | product offering should do so in a manner which does not create 153 | potential liability for other Contributors. Therefore, if a Contributor 154 | includes the Program in a commercial product offering, such Contributor 155 | ("Commercial Contributor") hereby agrees to defend and 156 | indemnify every other Contributor ("Indemnified Contributor") 157 | against any losses, damages and costs (collectively "Losses") 158 | arising from claims, lawsuits and other legal actions brought by a third 159 | party against the Indemnified Contributor to the extent caused by the 160 | acts or omissions of such Commercial Contributor in connection with its 161 | distribution of the Program in a commercial product offering. The 162 | obligations in this section do not apply to any claims or Losses 163 | relating to any actual or alleged intellectual property infringement. In 164 | order to qualify, an Indemnified Contributor must: a) promptly notify 165 | the Commercial Contributor in writing of such claim, and b) allow the 166 | Commercial Contributor to control, and cooperate with the Commercial 167 | Contributor in, the defense and any related settlement negotiations. The 168 | Indemnified Contributor may participate in any such claim at its own 169 | expense.
170 | 171 |For example, a Contributor might include the Program in a commercial 172 | product offering, Product X. That Contributor is then a Commercial 173 | Contributor. If that Commercial Contributor then makes performance 174 | claims, or offers warranties related to Product X, those performance 175 | claims and warranties are such Commercial Contributor's responsibility 176 | alone. Under this section, the Commercial Contributor would have to 177 | defend claims against the other Contributors related to those 178 | performance claims and warranties, and if a court requires any other 179 | Contributor to pay any damages as a result, the Commercial Contributor 180 | must pay those damages.
181 | 182 |5. NO WARRANTY
183 | 184 |EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 185 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 186 | OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 187 | ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 188 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 189 | responsible for determining the appropriateness of using and 190 | distributing the Program and assumes all risks associated with its 191 | exercise of rights under this Agreement , including but not limited to 192 | the risks and costs of program errors, compliance with applicable laws, 193 | damage to or loss of data, programs or equipment, and unavailability or 194 | interruption of operations.
195 | 196 |6. DISCLAIMER OF LIABILITY
197 | 198 |EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT 199 | NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 200 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 201 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 202 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 203 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 204 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 205 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
206 | 207 |7. GENERAL
208 | 209 |If any provision of this Agreement is invalid or unenforceable under 210 | applicable law, it shall not affect the validity or enforceability of 211 | the remainder of the terms of this Agreement, and without further action 212 | by the parties hereto, such provision shall be reformed to the minimum 213 | extent necessary to make such provision valid and enforceable.
214 | 215 |If Recipient institutes patent litigation against any entity 216 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 217 | Program itself (excluding combinations of the Program with other 218 | software or hardware) infringes such Recipient's patent(s), then such 219 | Recipient's rights granted under Section 2(b) shall terminate as of the 220 | date such litigation is filed.
221 | 222 |All Recipient's rights under this Agreement shall terminate if it 223 | fails to comply with any of the material terms or conditions of this 224 | Agreement and does not cure such failure in a reasonable period of time 225 | after becoming aware of such noncompliance. If all Recipient's rights 226 | under this Agreement terminate, Recipient agrees to cease use and 227 | distribution of the Program as soon as reasonably practicable. However, 228 | Recipient's obligations under this Agreement and any licenses granted by 229 | Recipient relating to the Program shall continue and survive.
230 | 231 |Everyone is permitted to copy and distribute copies of this 232 | Agreement, but in order to avoid inconsistency the Agreement is 233 | copyrighted and may only be modified in the following manner. The 234 | Agreement Steward reserves the right to publish new versions (including 235 | revisions) of this Agreement from time to time. No one other than the 236 | Agreement Steward has the right to modify this Agreement. The Eclipse 237 | Foundation is the initial Agreement Steward. The Eclipse Foundation may 238 | assign the responsibility to serve as the Agreement Steward to a 239 | suitable separate entity. Each new version of the Agreement will be 240 | given a distinguishing version number. The Program (including 241 | Contributions) may always be distributed subject to the version of the 242 | Agreement under which it was received. In addition, after a new version 243 | of the Agreement is published, Contributor may elect to distribute the 244 | Program (including its Contributions) under the new version. Except as 245 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives no 246 | rights or licenses to the intellectual property of any Contributor under 247 | this Agreement, whether expressly, by implication, estoppel or 248 | otherwise. All rights in the Program not expressly granted under this 249 | Agreement are reserved.
250 | 251 |This Agreement is governed by the laws of the State of New York and 252 | the intellectual property laws of the United States of America. No party 253 | to this Agreement will bring a legal action under this Agreement more 254 | than one year after the cause of action arose. Each party waives its 255 | rights to a jury trial in any resulting litigation.
256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject com.redbrainlabs/system-graph "0.3.0" 2 | :description "Graph + Component for large system composition" 3 | :url "https://github.com/redbrainlabs/system-graph" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[com.stuartsierra/component "0.3.0"] 7 | [prismatic/plumbing "0.5.0"]] 8 | :profiles {:dev {:plugins [[lein-midje "3.1.3-RC2"]] 9 | :dependencies [[org.clojure/tools.namespace "0.2.4"] 10 | [org.clojure/clojure "1.7.0"] 11 | [midje "1.7.0"]] 12 | :source-paths ["dev"]}} 13 | :aliases {"dumbrepl" ["trampoline" "run" "-m" "clojure.main/main"]}) 14 | -------------------------------------------------------------------------------- /src/com/redbrainlabs/system_graph.clj: -------------------------------------------------------------------------------- 1 | (ns com.redbrainlabs.system-graph 2 | (:require [clojure.set :as set] 3 | 4 | [com.stuartsierra.component :refer [Lifecycle] :as component] 5 | [plumbing.core :as plumbing] 6 | [plumbing.graph :as graph] 7 | [schema.core :as s] 8 | 9 | [com.redbrainlabs.system-graph.utils :refer [topo-sort comp-fnk fnk-deps]])) 10 | 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ;;; Private 13 | 14 | (defn- lifecycle-components 15 | "Filters the component-keys to only the vals of computed-system that satisfy Lifecycle. 16 | The filtering preseves the ordering of the original component-keys." 17 | [component-keys computed-system] 18 | (->> component-keys 19 | (map (fn [k] [k (get computed-system k)])) 20 | (filter (fn [[k component]] 21 | (and 22 | (satisfies? Lifecycle component) 23 | ;; this check is needed since 'component' now includes a default 24 | ;; impl on Object... but there is an implicit requirement that 25 | ;; the component is an IObj for metadata support. 26 | (isa? (class component) clojure.lang.IObj)))) 27 | (mapv first))) 28 | 29 | (def dependencies :com.stuartsierra.component/dependencies) 30 | 31 | (defn- attach-component-metadata [computed-system original-graph compiled-fnk lifecycle-comps] 32 | (let [lifecycle-comps (set lifecycle-comps) 33 | lifecycle-deps (->> lifecycle-comps 34 | (map (fn [k] 35 | (let [original-fnk (get original-graph k) 36 | original-dep-metadata (-> original-fnk meta dependencies) 37 | inferred-deps (-> original-fnk 38 | fnk-deps 39 | set 40 | (set/intersection lifecycle-comps) 41 | (set/difference (-> original-dep-metadata vals set)))] 42 | (merge (zipmap inferred-deps inferred-deps) original-dep-metadata)))) 43 | (zipmap lifecycle-comps))] 44 | (reduce-kv (fn [m* k deps] 45 | (update-in m* [k] component/using deps)) 46 | computed-system lifecycle-deps))) 47 | 48 | (defn- create-system-map [original-graph compiled-fnk computed-system] 49 | (let [lifecycle-comps (lifecycle-components (topo-sort original-graph) computed-system)] 50 | (-> computed-system 51 | (attach-component-metadata original-graph compiled-fnk lifecycle-comps) 52 | component/map->SystemMap))) 53 | 54 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 55 | ;;; Public 56 | 57 | (defn compile-as-system-graph 58 | "Compiles a Prismatic graph using `compile` and wraps the functionality so that 59 | the resulting fnk will return a 'component' SystemMap that complies with Lifecycle. 60 | 61 | All of the dependency information implicyt with the graph and fnks is carried over 62 | into the SystemMap and component vals using `component/using`. " 63 | [compile g] 64 | (if (fn? g) 65 | g 66 | (let [g (-> (plumbing/map-vals (partial compile-as-system-graph compile) g) graph/->graph) 67 | fnk (compile g)] 68 | (comp-fnk (partial create-system-map g fnk) fnk)))) 69 | 70 | (def eager-compile 71 | "Performs a #'plumbing.graph/eager-compile on the graph so that all the 72 | computed results from the compiled fnk are SystemGraphs which satisfy Lifecycle." 73 | (partial compile-as-system-graph graph/eager-compile)) 74 | 75 | (def eager-interpreted 76 | "Performs a #'plumbing.graph/eager-compile on the graph so that all the 77 | computed results from the compiled fnk are SystemGraphs which satisfy Lifecycle." 78 | (partial compile-as-system-graph graph/interpreted-eager-compile)) 79 | 80 | (defn init-system 81 | "Analogous to #'plumbing.graph/run, initializes the graph as a SystemGraph with the given input. " 82 | [g input] 83 | ((eager-interpreted g) input)) 84 | 85 | (defn start-system 86 | "Recursively starts the system components in the correct order as implicity defined in the graph." 87 | [system-graph] 88 | (component/start-system system-graph)) 89 | 90 | (defn stop-system 91 | "Recursively stops the system components in the reverse order in which they were started." 92 | [system-graph] 93 | (component/stop-system system-graph)) 94 | -------------------------------------------------------------------------------- /src/com/redbrainlabs/system_graph/utils.clj: -------------------------------------------------------------------------------- 1 | (ns com.redbrainlabs.system-graph.utils 2 | "Utillity fns for working with Prismatic's Graph and fnk" 3 | (:require [plumbing.graph :as graph] 4 | [schema.core :as s])) 5 | 6 | (defn topo-sort 7 | "Returns the topological sort of a Prismatic graph" 8 | [g] 9 | (-> g graph/->graph keys vec)) 10 | 11 | ;; TODO: see if plumbing already has a fn for this... or helper fns that seem less intrusive.. 12 | (defn fnk-deps [fnk] 13 | (->> fnk 14 | s/fn-schema 15 | :input-schemas ;; [[#schema.core.One{:schema {Keyword Any, :funk Any, :x Any}, :optional? false, :name arg0}]] 16 | ffirst 17 | :schema 18 | keys 19 | (filter keyword?))) 20 | 21 | ;; TODO: see if plumbing's `comp-partial` does what I need (as suggested by Jason Wolfe) 22 | (defn comp-fnk 23 | "Composes the given given fnk with the provided fn. Only handles the binary case." 24 | [f fnk] 25 | ;; TODO: handle other fnks (verifying input/output schemas) and the variadic case 26 | (let [comped (-> (comp f fnk) 27 | (with-meta (meta fnk)))] 28 | ;; compose the positional function as well if present 29 | (if [(-> fnk meta :plumbing.fnk.impl/positional-info)] 30 | (vary-meta comped update-in [:plumbing.fnk.impl/positional-info 0] (partial comp f)) 31 | comped))) 32 | -------------------------------------------------------------------------------- /test/com/redbrainlabs/system_graph/utils_test.clj: -------------------------------------------------------------------------------- 1 | (ns com.redbrainlabs.system-graph.utils-test 2 | (:require [midje.sweet :refer :all] 3 | [plumbing.core :refer [fnk]] 4 | [plumbing.graph :as graph] 5 | [schema.core :as s] 6 | 7 | [com.redbrainlabs.system-graph.utils :refer :all])) 8 | 9 | (facts "#'topo-sort" 10 | ;; sanity/characteristic test since we are relying on Graph's impl.. 11 | (topo-sort {:a (fnk [x] (inc x)) 12 | :b (fnk [a] (inc a)) 13 | :c (fnk [b] (inc b))}) => [:a :b :c]) 14 | 15 | (facts "#'comp-fnk" 16 | (let [square-fnk (fnk [x] (* x x)) 17 | with-inc (comp-fnk inc square-fnk)] 18 | 19 | (fact "composes the regular fn with the fnk" 20 | (with-inc {:x 5}) => 26) 21 | 22 | (fact "composes the positional fn in the metadata as well" 23 | ((-> with-inc meta :plumbing.fnk.impl/positional-info first) 5) => 26) 24 | 25 | (fact "preserves the original fnk's schema" 26 | (s/fn-schema with-inc) => (s/fn-schema square-fnk)))) 27 | 28 | (facts "#'fnk-deps" 29 | (fnk-deps (fnk [a b c])) => (just [:a :b :c] :in-any-order) 30 | (fact "works on compiled graphs" 31 | (-> {:a (fnk [x] x) :b (fnk [y] y)} graph/eager-compile fnk-deps) => (just [:x :y] :in-any-order))) 32 | -------------------------------------------------------------------------------- /test/com/redbrainlabs/system_graph_test.clj: -------------------------------------------------------------------------------- 1 | (ns com.redbrainlabs.system-graph-test 2 | (:require [com.stuartsierra.component :refer [Lifecycle] :as component] 3 | [plumbing.core :refer [fnk]] 4 | [midje.sweet :refer :all] 5 | 6 | [com.redbrainlabs.system-graph :refer :all])) 7 | 8 | 9 | (def lifecycle-sort :com.redbrainlabs.system-graph/lifecycle-sort) 10 | 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ;;; Integration tests... since this library is 100% glue 13 | 14 | (defrecord Lifecycler [!started !stopped key value] 15 | Lifecycle 16 | (start [this] 17 | (swap! !started conj key) 18 | this) 19 | (stop [this] 20 | (swap! !stopped conj key) 21 | this)) 22 | 23 | (facts "system-graph" 24 | (let [deps-started (atom []) 25 | deps-stopped (atom []) 26 | lifecycler (partial ->Lifecycler deps-started deps-stopped) 27 | subgraph {:x-squared (fnk [x] (lifecycler :x-squared (* x x))) 28 | :x-cubed (fnk [x x-squared] ;;(prn {:x x :x-squared x-squared}) 29 | (lifecycler :x-cubed (* x (:value x-squared))))} 30 | graph {:subgraph subgraph 31 | :y (fnk [[:subgraph x-cubed]] (lifecycler :y (inc (:value x-cubed)))) 32 | :x-inc (fnk [x] (lifecycler :x-inc (inc x))) 33 | :no-lifecycle (fnk [x-inc] :something-that-doesnt-implement-lifecycle)} 34 | system-graph (init-system graph {:x 4})] 35 | 36 | (fact "starts the lifecycle deps using a toposort" 37 | (component/start system-graph) 38 | @deps-started => [:x-squared :x-cubed :y :x-inc]) 39 | (fact "stops the deps in the opposite order" 40 | (component/stop system-graph) 41 | @deps-stopped => [:x-inc :y :x-cubed :x-squared]))) 42 | 43 | (defrecord DummyComponent [name started] 44 | Lifecycle 45 | (start [this] (assoc this :started true)) 46 | (stop [this] (assoc this :started false))) 47 | 48 | (defn dummy-component [name] 49 | (->DummyComponent name false)) 50 | 51 | (facts "dependent components" 52 | (let [graph {:a (fnk [] 53 | (dummy-component :a)) 54 | :b (fnk [a] 55 | (-> (dummy-component :b) 56 | (assoc :a a)))} 57 | system-graph (init-system graph {}) 58 | started-system (start-system system-graph)] 59 | (facts "are passed in to fnks before they are started" 60 | (:b system-graph) => (just {:a {:name :a, :started false}, :name :b, :started false})) 61 | (facts "are started and assoced onto lifecycle components before then dependent's #'start is called" 62 | (:b started-system) => (just {:a {:name :a, :started true}, :name :b, :started true})))) 63 | 64 | (facts "dependent components with different names that the system's names" 65 | (let [graph {:a (fnk [] 66 | (dummy-component :a)) 67 | :b (-> (fnk [a] 68 | (-> (dummy-component :b) 69 | (assoc :foo a))) 70 | (component/using {:foo :a}))} 71 | system-graph (init-system graph {}) 72 | started-system (start-system system-graph)] 73 | (facts "are passed in to fnks before they are started" 74 | (:b system-graph) => (just {:foo {:name :a, :started false}, :name :b, :started false})) 75 | (facts "are started and assoced onto lifecycle components with the different name before then dependent's #'start is called" 76 | (:b started-system) => (just {:foo {:name :a, :started true}, :name :b, :started true})))) 77 | 78 | (defrecord StatefulDummyComponent [name started !counter] 79 | Lifecycle 80 | (start [this] (swap! !counter update-in [:started] inc) (assoc this :started true)) 81 | (stop [this] (swap! !counter update-in [:stopped] inc) (assoc this :started false))) 82 | 83 | (defn stateful-dummy-component [name] 84 | (->StatefulDummyComponent name false (atom {:started 0 :stopped 0}))) 85 | 86 | (facts "systems can be started and stopped multiple times" 87 | (let [graph {:a (fnk [] 88 | (stateful-dummy-component :a)) 89 | :b (fnk [a] 90 | (-> (stateful-dummy-component :b) 91 | (assoc :a a)))} 92 | system-graph (init-system graph {}) 93 | cycled-system (reduce (fn [sys _] (-> sys start-system stop-system)) system-graph (range 5))] 94 | 95 | 96 | (-> cycled-system :a :!counter deref) => {:started 5, :stopped 5} 97 | (-> cycled-system start-system :b :a :started) => true 98 | (-> cycled-system start-system stop-system :b :started) => false 99 | ;; TODO: investgate this to see if this is a system-graph bug or component.. 100 | ;; it looks like component doesn't assoc the deps on stop.. but the newer version says it does.. 101 | ;; (-> cycled-system start-system stop-system :b :a :started) => false 102 | )) 103 | --------------------------------------------------------------------------------