├── .gitignore ├── .gitmodules ├── CHANGELOG.org ├── README.org ├── deps.edn ├── docs ├── css │ ├── et-book.css │ ├── grid.css │ ├── main.css │ └── post.css ├── fonts │ └── et-book │ │ ├── et-book-bold-line-figures │ │ ├── et-book-bold-line-figures.eot │ │ ├── et-book-bold-line-figures.svg │ │ ├── et-book-bold-line-figures.ttf │ │ └── et-book-bold-line-figures.woff │ │ ├── et-book-display-italic-old-style-figures │ │ ├── et-book-display-italic-old-style-figures.eot │ │ ├── et-book-display-italic-old-style-figures.svg │ │ ├── et-book-display-italic-old-style-figures.ttf │ │ └── et-book-display-italic-old-style-figures.woff │ │ ├── et-book-roman-line-figures │ │ ├── et-book-roman-line-figures.eot │ │ ├── et-book-roman-line-figures.svg │ │ ├── et-book-roman-line-figures.ttf │ │ └── et-book-roman-line-figures.woff │ │ ├── et-book-roman-old-style-figures │ │ ├── et-book-roman-old-style-figures.eot │ │ ├── et-book-roman-old-style-figures.svg │ │ ├── et-book-roman-old-style-figures.ttf │ │ └── et-book-roman-old-style-figures.woff │ │ └── et-book-semi-bold-old-style-figures │ │ ├── et-book-semi-bold-old-style-figures.eot │ │ ├── et-book-semi-bold-old-style-figures.svg │ │ ├── et-book-semi-bold-old-style-figures.ttf │ │ └── et-book-semi-bold-old-style-figures.woff ├── index.html ├── index.org └── js │ └── navigation.js ├── examples ├── README.org ├── boot │ ├── .gitignore │ ├── LICENSE │ ├── README.org │ ├── build.boot │ └── src │ │ └── example │ │ ├── core.clj │ │ ├── handler.clj │ │ ├── html.clj │ │ └── systems.clj └── leiningen │ ├── .gitignore │ ├── .lein-env │ ├── LICENSE │ ├── README.org │ ├── dev │ └── user.clj │ ├── doc │ └── intro.md │ ├── project.clj │ ├── src │ └── example │ │ ├── core.clj │ │ ├── handler.clj │ │ ├── html.clj │ │ └── systems.clj │ └── test │ └── example │ └── core_test.clj ├── meyvn.edn ├── project.clj ├── script └── pg_test_setup.sh ├── src └── system │ ├── boot.clj │ ├── components │ ├── adi.clj │ ├── aleph.clj │ ├── benjamin.clj │ ├── core_async_pipe.clj │ ├── core_async_pubsub.clj │ ├── datomic.clj │ ├── datomic_local.clj │ ├── durable_queue.clj │ ├── elasticsearch.clj │ ├── endpoint.clj │ ├── h2.clj │ ├── handler.clj │ ├── hara_io_scheduler.clj │ ├── hikari.clj │ ├── http_kit.clj │ ├── immutant_web.clj │ ├── jdbc.clj │ ├── jetty.clj │ ├── kampbell.clj │ ├── konserve.clj │ ├── lambdacd.clj │ ├── ldap.clj │ ├── mariadb.clj │ ├── mongo.clj │ ├── neo4j.clj │ ├── next_jdbc.clj │ ├── poco.clj │ ├── postgres.clj │ ├── quartzite.clj │ ├── rabbitmq.clj │ ├── redis.clj │ ├── redis_pubsub.clj │ ├── redis_queue.clj │ ├── repl_server.clj │ ├── riemann_client.clj │ ├── rj9.clj │ ├── scheduled_executor_service.clj │ ├── sente.cljc │ ├── undertow.clj │ └── watcher.clj │ ├── core.clj │ ├── monitoring │ ├── core.clj │ ├── jetty.clj │ ├── quartzite.clj │ ├── riemann_client.clj │ ├── scheduled_executor_service.clj │ └── watcher.clj │ ├── nrepl_middleware.clj │ ├── reload.clj │ ├── repl.clj │ └── schema.clj └── test └── system ├── cljs_runner.cljs ├── components ├── adi_test.clj ├── aleph_test.clj ├── benjamin_test.clj ├── core_async_pubsub_test.clj ├── datomic_test.clj ├── durable_queue_test.clj ├── elasticsearch_test.clj ├── etsy_test.clj ├── h2_test.clj ├── handler_test.clj ├── hara_io_scheduler_test.clj ├── hikari_test.clj ├── http_kit_test.clj ├── immutant_web_test.clj ├── jdbc_test.clj ├── jetty_test.clj ├── kampbell_test.clj ├── konserve_test.clj ├── lambdacd_test.clj ├── ldap_test.clj ├── mongo_test.clj ├── neo4j_test.clj ├── postgres_test.clj ├── quartzite_test.clj ├── redis_pubsub_test.clj ├── redis_queue_test.clj ├── redis_test.clj ├── repl_server_test.clj ├── riemann_client_test.clj ├── scheduled_executor_service_test.clj ├── sente_client_test.cljs ├── sente_test.clj └── watcher_test.clj └── schema_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /target 3 | /.classpath 4 | /.project 5 | /.lein-failures 6 | .#* 7 | .DS_Store 8 | .lein-repl-history 9 | .nrepl-port 10 | node_modules/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "examples/web-service"] 2 | path = examples/web-service 3 | url = git@github.com:danielsz/system-advanced-example.git 4 | [submodule "examples/dependency-injection"] 5 | path = examples/dependency-injection 6 | url = git@github.com:danielsz/system-dependency-injection.git 7 | [submodule "examples/system-duct-style-example"] 8 | path = examples/system-duct-style-example 9 | url = git@github.com:danielsz/system-duct-style-example.git 10 | [submodule "examples/system-websockets"] 11 | path = examples/system-websockets 12 | url = git@github.com:danielsz/system-websockets.git 13 | -------------------------------------------------------------------------------- /CHANGELOG.org: -------------------------------------------------------------------------------- 1 | * Changes 2 | ** 0.5.5 3 | - Init-fn for Mariadb component. 4 | - Support for Datomic local. Init-fn takes the Datomic connection as argument. 5 | ** 0.5.4 6 | - Support for multiple API handlers, which are siloed handlers with their own prefix, middleware and dependencies. 7 | ** 0.5.3 8 | - Support for info.sunng/ring-jetty9-adapter 9 | ** 0.5.2 10 | - Facilities to include an api handler behind prefix and separate middleware 11 | ** 0.5.1 12 | - Endpoints in Raamwerk, route data can have their per-endpoint middleware. 13 | ** 0.5.0 14 | - Raamwerk refinement with the endpoints. Now accepts route data or closures over Reitit routes with component dependencies in scope. 15 | ** 0.4.9 16 | - Raamwerk framework overhaul. Adoption of reitit as routing library. No need for middleware component, and handler component is greatly simplified. 17 | - Undertow web server 18 | ** 0.4.8 19 | - Mariadb: data-source and connection 20 | - core.async pipe: high-level component for a core.async pipe 21 | - Sente client fixes (ChannelSocketClient) 22 | - Bump dependencies (including tools.namespace and prismatic schema) 23 | ** 0.4.7 24 | - Etsy: configurable throttle rate 25 | - Immutant web: bug fix (stopping the server twice in a sequence would throw an error) 26 | - Meyvn config + dependencies updated to their latest versions 27 | ** 0.4.6 28 | - Benjamin: ability to wrap component for both persistence-fn and logbook-fn independently. 29 | - Mariadb: database connection component 30 | - Next.jdbc: database connection component 31 | - Defensive measure in durable queue component 32 | - Scheduled Executor Service: added one-off method for non-repeating tasks 33 | - Scheduled Executor Service: recursive scheduler for the one-off method (ability to schedule in the scheduled task) 34 | - Cleanup of Sente component 35 | - Option to get a throttled Etsy client 36 | ** 0.4.5 37 | - LDAP component (UnboundID LDAP SDK for Java) 38 | - New Redis queue component 39 | - Renamed Redis pub sub component (previously known a Carmine) 40 | - Bump dependency version for org.danielsz/lang-utils 41 | ** 0.4.4 42 | - Updated core dependencies (~lang-utils~, for example) 43 | - nREPL middleware for system (used by the Meyvn build tool). 44 | - Poco component is even simpler (no need to conj) 45 | ** 0.4.3 46 | - Sente security fix: https://github.com/ptaoussanis/sente/issues/137 47 | - Revamped Scheduled Executor Service component. Declarative. Starting jobs directly from the component. See test for usage. 48 | - Revamped Durable Queue. Declarative. Starting jobs directly from the component. See test for usage. 49 | - Poco: “plain old component”. Bare bones component. Conjes a map and that’s it. 50 | ** 0.4.2 51 | - Handler component has an option to disable shared root middleware 52 | - Fix: https://github.com/danielsz/system/issues/120 53 | - New Riemann client component 54 | - New Kampbell component 55 | - Repl server component will automatically detect the new [[https://github.com/nrepl/nREPL][nrepl]] namespaces if present. 56 | - Repl server component supports ~with-cider~ flag for Cider support 57 | - Cider Repl server component deprecated (merged with Repl server component) 58 | ** 0.4.1 59 | - REPL server with bind address option 60 | - Cider REPL server with bind address option 61 | - For danielsz/system users: Wondering if anyone know why ~(fn [req] (handler req))~ wraps the handler in line: https://github.com/danielsz/system/blob/master/src/system/components/jetty.clj#L13 62 | - rabbitmq try catch on close https://github.com/danielsz/system/pull/105 63 | - New core.async pubsub component with tests 64 | - Swapped routes and handlers in handler component: https://github.com/danielsz/system/issues/106 65 | - New durable queue component (Factual) 66 | - Konserve component accepts custom serializer + test 67 | - Konserve with carmine backend 68 | - Redis component with test 69 | - Benjamin component with test 70 | - Added new constructor signature to web components, supporting keyword arguments 71 | - Web server handlers can now be called anything (not just `:handler’), which allows to define any number of web servers in the system map 72 | - Support for alternative routing libraries (bidi) 73 | - lambdacd component https://github.com/danielsz/system/pull/114 74 | ** 0.4.0 75 | - Sente: Add options to component before wrapping, https://github.com/danielsz/system/pull/92/files 76 | - Alex Miller’s bad namespace form. https://github.com/danielsz/system/pull/93 & https://github.com/danielsz/system/pull/94 77 | - Regression fix. https://github.com/danielsz/system/issues/96 78 | - Parameter validations (auto and files, regexes and paths). 79 | - Hara Watcher idempotency https://github.com/danielsz/system/pull/99 80 | - Hara Watcher monitoring 81 | - Removed data_readers loading, since it is built-in the boot-repl task 82 | - Added modes to run system.boot in standard (Lisp) mode, no unloading (no remove-ns), or default tools.namespace mode (with remove-ns) 83 | - Etsy component: removed extraneous field 84 | - Removed dependencies from the dev profile in favor of test profile https://github.com/danielsz/system/issues/102 85 | - New Konserve component 86 | - Duct abstractions: enhancements 87 | 88 | - Endpoints can specify middleware 89 | - Routes will be recombined according to their middleware 90 | - The Handler component can also specify middleware that will be applied to all endpoints/routes 91 | 92 | This allows to address the use case when different routes need 93 | different middleware. For example, Sente is incompatible with 94 | `wrap-restful-format'. 95 | 96 | https://github.com/ptaoussanis/sente/issues/123 97 | 98 | Decomplecting routes and middleware also enables better 99 | composition. Side-effectful middleware, such as wrap-params, consumes 100 | the request body upon reading it. This means it should only be applied 101 | once. Hence, web-centric libraries should never force such middleware 102 | on the user, but rather let him apply any middleware needed on 103 | recombined routes in the end application. 104 | 105 | https://github.com/ring-clojure/ring-defaults/issues/10 106 | 107 | With our new implementation, it is possible to write libraries, for 108 | example a social sign-in library, keep it totally separate from 109 | application code, and then, with the Duct abstractions in `system', 110 | compose endpoints with any middleware the application should need. 111 | 112 | Middleware: https://github.com/danielsz/system/issues/48 113 | 114 | New, simplified middleware component. Receives a vector of 115 | functions or vectors. If vector is specified, apply first item in 116 | vector to handler and rest of vector items (middleware arguments). 117 | This is a breaking change (only for the middeware component). 118 | 119 | Middleware plays well in the dependency injection mechanism of Component. 120 | To wrap the component in middleware, use :component keyword (convention). 121 | - Duct components docstrings: https://github.com/danielsz/system/pull/103 122 | 123 | ** 0.3.1 124 | - Carmine/Redis PubSub (pattern channels) 125 | - hara.io.scheduler 126 | - Allow regex and paths in files vector (https://github.com/danielsz/system/pull/86) 127 | - Accomodate simplest use case (no system). http://stackoverflow.com/questions/38622722/reloading-from-clojure-file/38625722#38625722 128 | - Print output when system starts and stops (@peterromfeldhk) 129 | - Validate sys. https://github.com/danielsz/system/issues/91 130 | ** 0.3.0 131 | This is a breaking change release 132 | - Incorporates Duct abstractions (endpoint and handlers) 133 | - App component is deprecated 134 | - Revised the reloading mechanism. Leveraging tools.namespace. New option enabling to turn unloading on or off. 135 | - `reloaded.repl` is now `system.repl` 136 | - Validation with prismatic/schema 137 | - New example for system + sente 138 | - Mongo component supports options, credentials 139 | - Adi component added 140 | - Hikari component added 141 | - JDBC component made idempotent 142 | - Jetty made idempotent 143 | - Test selectors 144 | - Monitoring (protocol and various implementations) 145 | ** 0.2.1 146 | - Fixes a long-standing issue with *data-readers* in Boot: https://github.com/boot-clj/boot/issues/47 147 | - Release Datomic connection upon stop. https://github.com/danielsz/system/pull/46 148 | ** 0.2.0 149 | - Dependencies: clojure 1.7.0, sente 1.6.0 150 | - Cider nREPL component 151 | - Neo4j component 152 | - Quartzite component 153 | - ScheduledExecutorService component 154 | - Immutant web service component 155 | - Closures are used to expose resources to http requests instead of merging it in the request map (App component) 156 | - Web components (jetty, immutant, http-kit) can be passed functions, but also components. Yes, even arbitrary user-defined components, as long as the convention is respected, namely that (:app my-custom-component) returns a web handler. 157 | - Mongo component can be initialized with a function (typical use case: indices) that receives the db as argument 158 | ** 0.1.9 159 | - Generic app component for typical Ring applications 160 | - Web service example 161 | - Postgres component 162 | - Latest dependencies: component, ns-tracker. 163 | - http-kit acces to full options 164 | - EPL License 165 | ** 0.1.8 166 | - switch to enable hot-reloadable system 167 | - file-based granularity to only restart the system when user-specified files change. 168 | - auto-start option. 169 | - New ElasticSearch component 170 | ** 0.1.7 171 | - Example of a task to run a dev system on the command line (versus REPL). 172 | - ~run~ boot task is not built-in. So it now ships with ~system~. 173 | ** 0.1.6 174 | - Latest Sente version. This is a breaking change for Sente. 175 | ** 0.1.5 176 | - Added example project for the Boot build tool. 177 | - Added boot task to reload namespaces on file changes. 178 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * system 2 | 3 | 4 | [[https://clojars.org/org.danielsz/system/latest-version.svg]] 5 | 6 | *ProTip:* Snapshot versions may be available. 7 | 8 | [[https://danielsz.github.io/system][Documentation]]. 9 | 10 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {prismatic/schema {:mvn/version "1.4.1"} 2 | org.danielsz/lang-utils {:mvn/version "0.1.6"} 3 | org.clojure/tools.namespace {:mvn/version "1.5.0"} 4 | io.aviso/pretty {:mvn/version "1.4.4"} 5 | com.stuartsierra/component {:mvn/version "1.1.0"}}} 6 | -------------------------------------------------------------------------------- /docs/css/et-book.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @font-face { 4 | font-family: "et-book"; 5 | src: url("../fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.eot"); 6 | src: url("../fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.eot?#iefix") format("embedded-opentype"), url("../fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.woff") format("woff"), url("../fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.ttf") format("truetype"), url("../fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.svg#etbookromanosf") format("svg"); 7 | font-weight: normal; 8 | font-style: normal 9 | } 10 | 11 | @font-face { 12 | font-family: "et-book"; 13 | src: url("../fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.eot"); 14 | src: url("../fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.eot?#iefix") format("embedded-opentype"), url("../fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.woff") format("woff"), url("../fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.ttf") format("truetype"), url("../fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.svg#etbookromanosf") format("svg"); 15 | font-weight: normal; 16 | font-style: italic 17 | } 18 | 19 | @font-face { 20 | font-family: "et-book"; 21 | src: url("../fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.eot"); 22 | src: url("../fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.eot?#iefix") format("embedded-opentype"), url("../fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.woff") format("woff"), url("../fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.ttf") format("truetype"), url("../fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.svg#etbookromanosf") format("svg"); 23 | font-weight: bold; 24 | font-style: normal 25 | } 26 | -------------------------------------------------------------------------------- /docs/css/grid.css: -------------------------------------------------------------------------------- 1 | body { 2 | display: grid; 3 | grid-template-columns: repeat(12, 1fr); 4 | grid-template-rows: 20px 20px 1fr; 5 | margin:0; 6 | } 7 | 8 | #top { 9 | grid-column: 1 / -1; 10 | grid-row: 1; 11 | } 12 | 13 | #corner { 14 | grid-row: 1; 15 | grid-column: 1; 16 | } 17 | #lambda { 18 | grid-row: -2; 19 | grid-column: 1; 20 | align-self: end; 21 | } 22 | 23 | #content { 24 | grid-row: 3 / -1; 25 | grid-column: 1 / -1; 26 | } 27 | 28 | .square { 29 | width: 20px; 30 | height: 20px; 31 | } 32 | #square1 { 33 | grid-row: 1; 34 | grid-column: 1; 35 | } 36 | 37 | #square2 { 38 | 39 | grid-row-start: 2; 40 | grid-column-start: 2; 41 | 42 | } 43 | 44 | @media (min-width: 600px) { 45 | body{ 46 | grid-template-rows: 100px 100px 1fr; 47 | } 48 | #contra { 49 | 50 | grid-row-start: 2; 51 | grid-column-start: 2; 52 | } 53 | #left { 54 | 55 | grid-row: 1 / -1; 56 | grid-column: 1; 57 | } 58 | #content { 59 | grid-column: 3 / -1; 60 | } 61 | .square { 62 | width: 100px; 63 | height: 100px; 64 | } 65 | #lambda { 66 | grid-column: 2; 67 | padding: 2rem; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /docs/css/main.css: -------------------------------------------------------------------------------- 1 | .title { 2 | font-family: 'et-book', serif; 3 | font-size: 3rem; 4 | text-align: right !important; 5 | border-bottom: 1px solid black; 6 | padding-bottom: 2rem; 7 | margin-bottom: 2rem; 8 | } 9 | 10 | .subtitle { 11 | font-family: 'et-book', serif; 12 | font-size: 2rem; 13 | } 14 | 15 | ul { 16 | list-style-type: circle; 17 | } 18 | 19 | p, 20 | li { 21 | font-family: 'Source Sans Pro', sans-serif; 22 | font-size: 1.3em; 23 | font-weight: 300; 24 | } 25 | 26 | p { 27 | line-height: 1.4em; 28 | } 29 | 30 | blockquote > p { 31 | font-style: italic; 32 | } 33 | 34 | h1, 35 | h2, 36 | h3, 37 | h4, 38 | h5, 39 | h6 { 40 | font-family: 'Source Sans Pro', sans-serif; 41 | font-weight: 600; 42 | } 43 | 44 | h3 { 45 | font-size: 1.4em; 46 | } 47 | 48 | h4 { 49 | font-size: 1em; 50 | text-transform: uppercase; 51 | } 52 | 53 | #text-table-of-contents { 54 | text-transform: uppercase; 55 | } 56 | 57 | #text-table-of-contents > ul { 58 | list-style-type: none; 59 | } 60 | 61 | #table-of-contents > h2 { 62 | display: none; 63 | } 64 | 65 | #text-table-of-contents li:not(:last-child) { 66 | margin-bottom: 0.3em; 67 | } 68 | 69 | #text-table-of-contents a { 70 | text-decoration: none; 71 | color: inherit; 72 | } 73 | 74 | #text-table-of-contents a:visited { 75 | color: inherit; 76 | } 77 | 78 | div.outline-2, 79 | div.outline-text-2, 80 | #postamble { 81 | display: none; 82 | } 83 | 84 | pre.src { 85 | overflow: auto !important; 86 | } 87 | 88 | pre.src::before { 89 | top: 0 !important; 90 | } 91 | 92 | @media (min-width: 800px) { 93 | #text-table-of-contents > ul { 94 | column-count: 2; 95 | column-gap: 3rem; 96 | column-rule: 1px solid #c79c87; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /docs/css/post.css: -------------------------------------------------------------------------------- 1 | #content { 2 | display: grid; 3 | grid-template-columns: [full-start] minmax(1em, 2fr) [main-start] minmax(0, 38em) [main-end] minmax(1em, 1fr) [full-end]; 4 | } 5 | 6 | #content > * { 7 | grid-column: main; 8 | } 9 | 10 | #content > .org-src-container { 11 | grid-column: full; 12 | } 13 | 14 | #content > blockquote { 15 | grid-column: full; 16 | } 17 | 18 | #org-div-home-and-up { 19 | grid-column-start: -2; 20 | text-align: center; 21 | } 22 | 23 | #content img { 24 | max-width: 100%; 25 | } 26 | -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.eot -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.ttf -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-bold-line-figures/et-book-bold-line-figures.woff -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.eot -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.ttf -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.woff -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.eot -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.ttf -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-roman-line-figures/et-book-roman-line-figures.woff -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.eot -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.ttf -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.woff -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.eot -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.ttf -------------------------------------------------------------------------------- /docs/fonts/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielsz/system/2df7f93b1e79986b31a1d68b9a6d7fe0bb662579/docs/fonts/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.woff -------------------------------------------------------------------------------- /docs/index.org: -------------------------------------------------------------------------------- 1 | #+title: system 2 | #+SUBTITLE: Reloaded components à la carte 3 | #+OPTIONS: toc:1 num:nil 4 | #+HTML_HEAD: 5 | #+HTML_HEAD: 6 | #+HTML_HEAD: 7 | #+HTML_HEAD: 8 | #+HTML_HEAD: 9 | 10 | * What is it? 11 | *system* owes to [[https://github.com/stuartsierra/component][component]] both in spirit and in name. The component library helps implement the reloaded pattern as championed by Stuart Sierra. *system* is built on top of it, offering a set of readymade components. The idea is to expand the set over time with contributions from the community. It currently includes: 12 | 13 | - [[https://github.com/ring-clojure/ring][Jetty]] (HTTP server) 14 | - [[http://http-kit.org/][HTTP kit]] (Async HTTP server) 15 | - [[https://github.com/ztellman/aleph][aleph]] (Async HTTP server) 16 | - [[http://immutant.org/][Immutant Web]] (Web component in application server suite) 17 | - [[http://www.datomic.com/][Datomic]] (Immutable database) 18 | - [[http://docs.caudate.me/adi/][Adi]] (Datomic interface) 19 | - [[http://www.h2database.com/][H2]] (H2 relational database) 20 | - [[http://www.postgresql.org][PostgreSQL]] (SQL Database) 21 | - [[https://mariadb.com/][MariaDB]] (SQL Database) 22 | - [[https://github.com/seancorfield/next-jdbc][next-jdbc]] (JDBC wrapper) 23 | - [[http://clojuremongodb.info/][Monger]] (MongoDB client) 24 | - [[http://clojureneo4j.info/][Neo4j]] (Graph database) 25 | - [[http://clojurequartz.info/][Quartzite]] (Quartz Scheduler) 26 | - [[https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html][ScheduledExecutorService]] (Java scheduler) 27 | - [[https://github.com/ptaoussanis/sente][Sente]] (Websockets/Ajax communications library) 28 | - [[https://github.com/clojure/tools.nrepl][nREPL]] (Clojure network REPL) 29 | - [[http://clojurerabbitmq.info/][Langohr]] (RabbitMQ client) 30 | - [[https://github.com/danielsz/etsy-clojure-api][Etsy]] (Etsy client) 31 | - [[http://docs.caudate.me/hara/#haraiowatch][hara.io.watch]] (File watcher) 32 | - [[http://docs.caudate.me/hara/hara-io-scheduler.html][hara.io.scheduler]] (Cron-like scheduler) 33 | - [[https://www.elastic.co/][Elasticsearch]] (Elasticsearch full-text search engine) 34 | - [[https://github.com/danielsz/system/wiki/Raamwerk][Raamwerk]] ([[https://github.com/weavejester/duct][Duct-style]] components for Ring applications) 35 | - [[https://github.com/clojure/java.jdbc][clojure.java.jdbc]] (Low level JDBC access for Clojure) 36 | - [[https://github.com/tomekw/hikari-cp][Hikari CP]] (JDBC connection pool) 37 | - [[https://github.com/ptaoussanis/carmine][Redis]] (Redis connection) 38 | - [[https://github.com/ptaoussanis/carmine#listeners--pubsub][Redis pubsub]] (Redis PubSub) 39 | - [[https://github.com/ptaoussanis/carmine#message-queue][Redis queue]] (Redis queue) 40 | - [[https://github.com/clojure/core.async/wiki/Pub-Sub][core.async PubSub]] (Channel based pubbing and subbing) 41 | - [[https://github.com/replikativ/konserve][Konserve]] (Document store protocol) 42 | - [[https://github.com/danielsz/kampbell][Kampbell]] (Entity layer on top of key-value stores) 43 | - [[https://github.com/Factual/durable-queue][Durable Queue]] (Factual’s disk-backed task queue) 44 | - [[https://github.com/danielsz/benjamin][Benjamin]] (Idempotency with side-effects) 45 | - [[http://www.lambda.cd/][LambdaCD]] (build pipelines as code) 46 | - [[https://github.com/riemann/riemann-clojure-client][Riemann client]] (client for the network event stream processing system) 47 | - [[https://ldap.com/unboundid-ldap-sdk-for-java/][LDAP]] (Directory services) 48 | * Motivation 49 | A good REPL experience is a prerogative of Lisp languages. [[https://github.com/stuartsierra/reloaded][Reloaded]] components enable this goodness in Clojureland. Since they require an amount of setup, the first steps when starting a new project are generally devoted to infrastructure. My first attempt to tackle the boilerplate was a Leiningen [[https://github.com/danielsz/back-end-template][template]]. The problem is that Leiningen templates are hard to maintain and difficult to retrofit on existing projects. I was finding myself repeatedly updating the template for future use. Then it dawned on me that a library would better fit the bill. And so *system* came to light. It’s now the first dependency I add to any project, allowing me to focus from the get-go on the substance of my application. 50 | * Is it good? 51 | [[https://news.ycombinator.com/item?id=3067434][Yes.]] 52 | * Examples 53 | 54 | Example projects are available under the [[https://github.com/danielsz/system/tree/master/examples][examples]] folder. 55 | 56 | ** Get-started projects 57 | 58 | Two minimal projects that will help you get started. Both the [[https://github.com/danielsz/system/tree/master/examples/leiningen][Leiningen]] and [[https://github.com/danielsz/system/tree/master/examples/boot][Boot]] toolchain are covered. 59 | 60 | ** Real-world web service 61 | 62 | A web service written multiple times using different styles. 63 | 64 | - System map is accessed directly. https://github.com/danielsz/system-advanced-example 65 | - Dependency injection, Duct abstractions and other known techniques. https://github.com/danielsz/system-dependency-injection 66 | 67 | ** Websockets 68 | 69 | A web application with client-side UI. Demonstrates login procedure with Sente (a realtime web comms library). 70 | 71 | - https://github.com/danielsz/system-websockets 72 | 73 | ** Raamwerk 74 | A Ring application is composed with separate API and site middleware. 75 | 76 | - Applying separate middleware to corresponding routes. [[https://github.com/danielsz/system-api-example][https://github.com/danielsz/system-api-example]] 77 | - Running multiple handlers separately. [[https://github.com/danielsz/system-api-immutant-example][https://github.com/danielsz/system-api-immutant-example]] 78 | * Usage 79 | 80 | First, assemble your system(s) in a namespace of your choosing. Here we define two systems, development and production. 81 | #+BEGIN_SRC clojure 82 | (ns my-app.systems 83 | (:require 84 | [com.stuartsierra.component :as component] 85 | (system.components 86 | [jetty :refer [new-web-server]] 87 | [repl-server :refer [new-repl-server]] 88 | [datomic :refer [new-datomic-db]] 89 | [mongo :refer [new-mongo-db]]) 90 | [my-app.webapp :refer [handler]] 91 | [environ.core :refer [env]])) 92 | 93 | (defn dev-system 94 | "Assembles and returns components for an application in production" 95 | [] 96 | (component/system-map 97 | :datomic-db (new-datomic-db (env :db-url)) 98 | :mongo-db (new-mongo-db) 99 | :web (new-web-server (env :http-port) handler))) 100 | 101 | (defn prod-system 102 | "Assembles and returns components for a production deployment" 103 | [] 104 | (merge (dev-system) 105 | (component/system-map 106 | :repl-server (new-repl-server (env :repl-port)))) 107 | 108 | #+END_SRC 109 | 110 | Then, in `user.clj`: 111 | 112 | #+BEGIN_SRC clojure 113 | (ns user 114 | (:require [system.repl :refer [system set-init! start stop reset]] 115 | [my-app.systems :refer [dev-system]])) 116 | 117 | (set-init! #'dev-system) 118 | #+END_SRC 119 | You can now manipulate the system in the REPL: ~(start)~, ~(reset)~ or ~(stop)~. The system map is accessible at any time, it resides in a var named, as you can guess, ~#'system~. 120 | 121 | In production, in `core.clj`: 122 | 123 | #+BEGIN_SRC clojure 124 | (ns my-app.core 125 | (:gen-class) 126 | (:require [system.repl :refer [set-init! start]] 127 | [my-app.systems :refer [prod-system]])) 128 | 129 | (defn -main 130 | "Start the application" 131 | [] 132 | (set-init! #'prod-system) 133 | (start)) 134 | #+END_SRC 135 | 136 | *** defsystem 137 | 138 | A convenience macro, ~defsystem~, allows you to declare systems succinctly: 139 | 140 | #+BEGIN_SRC clojure 141 | (ns my-app.systems 142 | (:require 143 | [system.core :refer [defsystem]] 144 | (system.components 145 | [jetty :refer [new-web-server]] 146 | [repl-server :refer [new-repl-server]] 147 | [datomic :refer [new-datomic-db]] 148 | [mongo :refer [new-mongo-db]]) 149 | [my-app.webapp :refer [handler]] 150 | [environ.core :refer [env]])) 151 | 152 | (defsystem dev-system 153 | [:datomic-db (new-datomic-db (env :db-url)) 154 | :mongo-db (new-mongo-db) 155 | :web (new-web-server (env :http-port) handler)]) 156 | 157 | (defsystem prod-system 158 | [:datomic-db (new-datomic-db (env :db-url)) 159 | :mongo-db (new-mongo-db (env :mongo-url)) 160 | :web (new-web-server (env :http-port) handler) 161 | :repl-server (new-repl-server (env :repl-port))]) 162 | 163 | #+END_SRC 164 | *Note:* Component allows you to define dependency relationships within systems. Please don’t use said macro for those cases. Be sure to consult component’s API to see the range of options available to you. 165 | 166 | *** At runtime: global system map vs dependency injection 167 | 168 | At runtime, the *system* var can be used anywhere after requiring it from the *system.repl* namespace: 169 | 170 | #+BEGIN_SRC clojure 171 | (ns front-end.webapp.handler 172 | (:require [system.repl :refer [system]])) 173 | 174 | (code-using system ...) 175 | #+END_SRC 176 | 177 | Note this pattern of directly accessing the global system var is in contrast with the pattern of dependency injection integral to Stuart Sierra's vision of Component. In this perspective, /components are defined in terms of the components on which they depend/. *system*, as a repository of readymade, reusable components, cannot and does not anticipate all the possible ways in which users will want to assemble components together. What it can and does, however, is anticipate common scenarii. Like your typical Ring application, for [[https://github.com/danielsz/system-dependency-injection][example]], where you may want to inject the database in the routes, so that it is readily available when serving http requests. 178 | 179 | *system* ships with a web framework, ~Raamwerk~, inspired by the [[https://github.com/weavejester/duct][Duct]] framework: the ~endpoint~, ~middleware~ and ~handler~ components. The ~endpoint~ component returns routes that are closed over by the component passed to it, so that its constituents are accessible via standard map destructuring. The rationale for this is explained [[https://www.booleanknot.com/blog/2015/05/22/structuring-clojure-web-apps.html][here]]. If the previous sentence didn’t sound agreeable, I suggest you check out the [[https://github.com/danielsz/system/wiki/Raamwerk][wiki]] and the examples. 180 | 181 | The ability to decompose a web application in mulitple ~endpoints~ offers flexibility and opportunies of reuse. For example, you can isolate functionality in library projects, and join the ~endpoints~ in the target application’s unified ~handler~. The possibilities are numerous. 182 | 183 | Documentation for Raamwerk’s components is [[http://danielsz.github.io/system/][available]]. 184 | #+BEGIN_QUOTE 185 | As with many patterns, DI can be abused. It is easy to get carried away with dependency injection and build a towering dependency graph that is unnecessary and even counter-productive. — Ben Morris in [[http://www.ben-morris.com/how-not-to-use-dependency-injection-service-locators-and-injection-mania/][How not to use dependency injection: service locators and injection mania.]] 186 | #+END_QUOTE 187 | 188 | Whatever you do, use your best judgment. 189 | 190 | * Boot-system 191 | ~System~ and ~Boot~ are a match made in heaven. Some of the properties that boot-system brings to your workflow are: 192 | 193 | - Manual and automatic mode, ie. either you manipulate the system in the REPL, or you configure it to react to editing changes. 194 | - Restartable system. What warrants a system restart is user-configurable. File-based granularity. 195 | - Changes that do not require a restart are available in the running system instantly (via namespace reloading). 196 | - Full /Lisp-style/ interactive programming via the REPL and hot-reloading in the browser. 197 | 198 | The ~system~ task is invoked like any ~boot~ task. 199 | #+BEGIN_SRC shell 200 | $ boot system -h 201 | #+END_SRC 202 | 203 | Which outputs, for example: 204 | 205 | #+BEGIN_SRC shell 206 | -h, --help Print this help info. 207 | -s, --sys SYS Set the system var to SYS. 208 | -a, --auto Manages the lifecycle of the application automatically. 209 | -f, --files FILES Will reset the system if a filename in the supplied vector changes. 210 | -r, --regexes Treat --files as regexes, not file names. Only one of regexes|paths is allowed. 211 | -p, --paths Treat --files as classpath paths, not file names. Only one of regexes|paths is allowed. 212 | #+END_SRC 213 | 214 | When ~auto~ is set to true, reloading of namespaces and restarts are being managed automatically. 215 | 216 | If you set ~auto~ to false, you indicate that you want to handle restarts manually at the REPL, with ~(system.repl/reset)~. Please note that SYS will be initialized and started for you at first run. 217 | 218 | If you don’t supply a SYS argument, the system task will act as a ~tools.namespace~ wrapper. Each time you save your file, affected namespaces are reloaded in dependency order (after being unloaded in reverse order). This is handy for projects that do not use ~Component~, like this [[https://github.com/danielsz/no-restarts][example]] project. 219 | 220 | ** Code reload vs system restart 221 | 222 | In traditional Lisp systems, users can redefine arbitrary, discrete units of code. Clojure, as a Lisp, is no exception. However, as a hosted language with many advanced dynamic features, code reloading has many [[https://github.com/clojure/tools.namespace#reloading-code-motivation][pitfalls]]. ~tools.namespace~ fixes many of them, but ultimately, reloaded code will not agree with runtime state, and the system will need a full restart. This is where ~component~ fits in. (Note that both libraries were authored by Stuart Sierra). 223 | 224 | It is important to understand that code reloading and system restarts are orthogonal—both are desirable properties in a programming environment. A full restart is a blunt tool. No need to restart the database just because a helper function was modified. 225 | 226 | We *don’t want* to restart the system with *every* code change. Ideally, we want to only reload the code that has changed, and occasionally restart (when necessary). 227 | 228 | ~boot-system~ gives you finegrained tuning over system restarts vs code reload. Each time you change a file, ~boot-system~ will reload your code. Conversely, filenames that have been designated in the ~files~ option will trigger a full restart. Typically, the files vector will be small, as most modifications do not warrant a full restart. An example of when you would want a full restart is when you modify a Var that is used in a thread (that of a web server, for example). This is explained in detail in the [[http://danielsz.github.io/2016/05/06/Ghosts-in-the-machine][Ghosts in the machine]] blog post. 229 | Check the options with ~boot system -h~. 230 | 231 | In summary, when you instruct ~boot-system~ to manage your application lifecycle (with the ~auto~ option), either one of those two things will happen after you change a source file: 232 | - ~refresh~ will first unload all affected namespaces (to remove old definitions) before reloading them in dependency order. 233 | - ~reset~ will restart the system if that file was defined in the ~files~ vector. 234 | 235 | ** The Holy Grail 236 | 237 | With ~system~, you can enjoy a true Lisp environment where code is always live (*live coding*). A tutorial is available in a separate repository. It is dubbed the [[https://github.com/danielsz/holygrail][Holy Grail]], because the solution encompasses back-end and front-end. Some people like to start off from the setup in that repo as a fast track for clj/cljs combo projects. For them, Akash Shakdwipeea has made a [[https://github.com/shakdwipeea/system-template][Boot template]] available. 238 | 239 | ** Leiningen 240 | 241 | If you are using Leiningen, we recommend [[https://github.com/bhauman/lein-figwheel][Figwheel]] to address browser-side hot-reloading concerns. 242 | 243 | * Monitoring 244 | 245 | A monitoring protocol is available to query the status of 246 | components. Two methods are available, ~started?~ and ~stopped?~, 247 | whose concrete implementations depend on the native APIs of the 248 | service behind the component. 249 | 250 | * The Reloaded pattern 251 | Here are a couple of links that are sure to shed more light on the motivations of the reloaded workflow. 252 | 253 | ** The canonical reference: 254 | [[http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded][My Clojure Workflow, Reloaded]] 255 | 256 | ** Interactive programming 257 | I gave a talk at several Clojure user groups (Belgium, Spain, Israel). BeClojure did a great job at recording it and making it available on Youtube. Mattias Buelens also produced a very nice [[http://mattiasbuelens.github.io/interactiveprogrammingtalk/interactiveprogramming.html][interactive UI]] for the BeClojure talk. 258 | 259 | #+HTML: Interactive programming 260 | 261 | ** Additional references 262 | And more references touching on the topic. 263 | - [[http://www.infoq.com/presentations/Clojure-Large-scale-patterns-techniques][Clojure in the Large]] 264 | - [[http://martintrojer.github.io/clojure/2013/09/07/retrofitting-the-reloaded-pattern-into-clojure-projects/][Retrofitting the Reloaded pattern into Clojure projects]] 265 | - [[http://software-ninja-ninja.blogspot.co.il/2014/04/5-faces-of-dependency-injection-in.html][5 faces of dependency injection in Clojure]] 266 | - [[https://github.com/weavejester/reloaded.repl][REPL functions to support the reloaded workflow]] 267 | 268 | * Compatibility 269 | There is a host of components libraries in the Clojure ecosystem, each with its own take, its own philosophy. For example: 270 | 271 | - [[https://github.com/juxt/modular][modular]] 272 | - [[https://github.com/palletops/leaven][leaven]] and [[https://github.com/palletops/bakery][bakery]] 273 | - [[https://github.com/james-henderson/yoyo][yoyo]] 274 | - [[http://docs.caudate.me/hara/#haracomponent][hara.component]] 275 | - [[https://github.com/tolitius/mount][mount]] 276 | 277 | Navigating this space can be difficult or overwhelming. Due to the nature of Open Source Software, it is unlikely to see any kind of standardization taking place. Let’s embrace the diversity instead, and emphasize the *compatibility* of components. As long as a component adheres to Stuart Sierra’s Lifecycle protocol, you can import it in your ~systems~ namespace and refer to it as any other native ~system~ component. 278 | 279 | *** Choosing 280 | 281 | To help choose if ~system~ is right for you, here are a couple of tips. Take a component for an often used dependency (a web server, for example, or a database), and compare their source code. The ~system~ library puts an emphasis on two properties: 282 | 283 | - minimalism: ~system~ provides a way to instantiate components that fulfill the Licecycle protocol (~start~ and ~stop~). Nothing more, nothing less. 284 | - Interactive programming: ~system~ is best used in a Lispy, interactive workflow, hence its deep integration with boot. 285 | 286 | * Contributing 287 | Please fork and issue a pull request to add more components. Please 288 | don't forget to include tests. You can refer to the existing ones to 289 | get started. 290 | 291 | Calling ~lein test~ will tests that have no external 292 | dependencies. Tests that do require external services being installed 293 | on your system (such as Mongo, Postgres or Elasticsearch) can be run 294 | with ~lein test :dependency~. Use ~lein test :all~ to run the full 295 | test suite. 296 | 297 | * Credits 298 | I wish to thank [[https://github.com/stuartsierra][Stuart Sierra]] for the pioneering and guidance. Special thanks to [[https://github.com/weavejester][James Reeves]] for the [[https://github.com/weavejester/reloaded.repl][reloaded.rep]]l library and general inspiration. Thanks to [[https://github.com/ptaoussanis][Peter Taoussanis]], the friendly OSS contributor, who helped to ‘componentize’ [[https://github.com/ptaoussanis/sente][sente]], an amazing library on its own right. 299 | * License 300 | Distributed under the [[http://opensource.org/licenses/eclipse-1.0.php][Eclipse Public License]] (the same as Clojure) together with the [[https://996.icu/#/en_US][966.icu]] [[https://github.com/996icu/996.ICU/blob/master/LICENSE][license]]. 301 | 302 | [[https://img.shields.io/badge/link-996.icu-red.svg][https://img.shields.io/badge/link-996.icu-red.svg]] 303 | 304 | -------------------------------------------------------------------------------- /docs/js/navigation.js: -------------------------------------------------------------------------------- 1 | function hideAll() { 2 | const els = document.querySelectorAll(".outline-text-2, .outline-2"); 3 | els.forEach(el => { el.style.display = "none";}); 4 | } 5 | function tocAll() { 6 | const els = document.querySelectorAll("#text-table-of-contents ul li a"); 7 | els.forEach(el => { 8 | el.style.fontWeight = 300;}); 9 | } 10 | 11 | window.onpopstate = function(event) { 12 | var el = document.querySelector("a[href='" + document.location.hash + "']"); 13 | // console.log(el); 14 | hideAll(); 15 | tocAll(); 16 | el.style.fontWeight = 400; 17 | let section = document.location.hash.substring(1); 18 | const container = document.getElementById("outline-container-" + section); 19 | container.style.display = "block"; 20 | for (let i = 0; i < container.children.length; i++) { 21 | ( i === 0 ) ? container.children[i].style.display = "none" : container.children[i].style.display = "block"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/README.org: -------------------------------------------------------------------------------- 1 | * Examples 2 | 3 | ** Simple example 4 | 5 | Please select your preferred toolchain, [[https://github.com/danielsz/system/tree/master/examples/leiningen][Leiningen]] or [[https://github.com/danielsz/system/tree/master/examples/boot][Boot]]. Enjoy! 6 | 7 | ** Web service 8 | 9 | A full web service with CRUD functionality is presented here. Two versions of the same example are available, demonstrating different ways to make components work together. 10 | - [[https://github.com/danielsz/system-dependency-injection][DI]]: The officially sanctioned dependency injection pattern according to Stuart’s component library. 11 | - [[https://github.com/danielsz/system-advanced-example][Global]]: The system map as a global environment-like map. 12 | 13 | ** Holy grail 14 | 15 | [[https://github.com/danielsz/holygrail][Demo + screencast]] demonstrating the live, Lispy, REPL-y experience made possible with boot + system. 16 | -------------------------------------------------------------------------------- /examples/boot/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /examples/boot/LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /examples/boot/README.org: -------------------------------------------------------------------------------- 1 | * Example project 2 | 3 | ** What is this? 4 | This project gives you an overview of how you might want to structure a Clojure web app with the [[https://github.com/danielsz/system/][system]] library. 5 | The example project starts a web server in development mode, and a web server + remote repl in production. 6 | The build tool used in this project is [[http://boot-clj.com/][Boot]]. 7 | ** Instructions 8 | *** Development 9 | 10 | Run the development pipeline from the command line: 11 | #+BEGIN_SRC bash 12 | $ boot dev 13 | #+END_SRC 14 | 15 | Which is actually a ~boot~ task, defined in ~build.boot~: 16 | 17 | #+BEGIN_SRC clojure 18 | (deftask dev 19 | "Run a restartable system in the Repl" 20 | [] 21 | (comp 22 | (environ :env {:http-port 3000}) 23 | (watch :verbose true) 24 | (system :sys #'dev-system :auto true) 25 | (repl :server true))) 26 | #+END_SRC 27 | 28 | The ~:auto~ option tells ~system~ to start your application and automatically reload namespaces when you change them. You can manage restarts manually if you prefer. In that case, connect to your REPL from your favorite editor, and in the boot.user namespace, type: 29 | 30 | #+BEGIN_SRC clojure 31 | boot.user=> (go) 32 | #+END_SRC 33 | 34 | You’re all set now. Your local web app can be found on [[http://localhost:300][localhost]] on port 3000. Now you can edit your source files and make changes. Save and restart your application whenever you want a clean slate: 35 | 36 | #+BEGIN_SRC clojure 37 | boot.user=> (reset) 38 | #+END_SRC 39 | 40 | _Note_: If you have set ~:auto~ to true, you don’t need to restart manually. Namespaces are reloaded when source files are saved, and the ~:files~ option allow you to specify files that warrant a restart at the component level. 41 | 42 | If you want to run your development system from the command line (without opening a REPL), type: 43 | #+BEGIN_SRC sh 44 | $ boot dev-run 45 | #+END_SRC 46 | 47 | _Note_: Please make sure ~build.boot~ requires the system var in its namespace declarations. 48 | 49 | *** Production 50 | Package an uberjar: 51 | #+BEGIN_SRC sh 52 | $ boot build 53 | #+END_SRC 54 | 55 | You are ready to launch: 56 | #+BEGIN_SRC sh 57 | $ java -jar -Dhttp.port=8000 -Drepl.port=8001 target/myproject-1.0.0.jar 58 | #+END_SRC 59 | 60 | Your web app will be found on port 8000, and on port 8001 you will be able to connect to its repl for remote debugging. 61 | -------------------------------------------------------------------------------- /examples/boot/build.boot: -------------------------------------------------------------------------------- 1 | (set-env! 2 | :resource-paths #{"src"} 3 | :dependencies '[[org.danielsz/system "0.4.1"] 4 | 5 | [environ "1.1.0"] 6 | [boot-environ "1.1.0"] 7 | 8 | [ring "1.6.2"] 9 | [ring/ring-defaults "0.3.1"] 10 | [compojure "1.6.0"] 11 | 12 | [org.clojure/tools.nrepl "0.2.12"]]) 13 | 14 | (require 15 | '[environ.boot :refer [environ]] 16 | '[example.systems :refer [dev-system]] 17 | '[system.boot :refer [system run]] 18 | '[system.repl :refer [go reset]]) 19 | 20 | (deftask dev 21 | "Run a restartable system in the Repl" 22 | [] 23 | (comp 24 | (environ :env {:http-port "3000"}) 25 | (watch :verbose true) 26 | (system :sys #'dev-system :auto true :files ["handler.clj" "html.clj"]) 27 | (repl :server true))) 28 | 29 | (deftask dev-run 30 | "Run a dev system from the command line" 31 | [] 32 | (comp 33 | (environ :env {:http-port "3000"}) 34 | (run :main-namespace "example.core" :arguments [#'dev-system]) 35 | (wait))) 36 | 37 | (deftask build 38 | "Builds an uberjar of this project that can be run with java -jar" 39 | [] 40 | (comp 41 | (aot :namespace '#{example.core}) 42 | (pom :project 'myproject 43 | :version "1.0.0") 44 | (uber) 45 | (jar :main 'example.core))) 46 | -------------------------------------------------------------------------------- /examples/boot/src/example/core.clj: -------------------------------------------------------------------------------- 1 | (ns example.core 2 | (:gen-class) 3 | (:require 4 | [system.repl :refer [set-init! start]] 5 | [example.systems :refer [prod-system]])) 6 | 7 | (defn -main 8 | "Start a production system." 9 | [& args] 10 | (let [system (or (first args) #'prod-system)] 11 | (set-init! system) 12 | (start))) 13 | -------------------------------------------------------------------------------- /examples/boot/src/example/handler.clj: -------------------------------------------------------------------------------- 1 | (ns example.handler 2 | (:require 3 | [compojure.route :as route] 4 | [compojure.core :refer [defroutes GET]] 5 | [ring.middleware.defaults :refer [wrap-defaults site-defaults]] 6 | [ring.util.response :refer [response content-type]] 7 | [example.html :as html])) 8 | 9 | (defroutes routes 10 | (GET "/" [] (html/index)) 11 | (GET "/test" [] (-> (response "Example.") 12 | (content-type "text/plain"))) 13 | (route/not-found "

Page not found

")) 14 | 15 | (def app 16 | (wrap-defaults #'routes site-defaults)) 17 | 18 | -------------------------------------------------------------------------------- /examples/boot/src/example/html.clj: -------------------------------------------------------------------------------- 1 | (ns example.html 2 | (:require 3 | (hiccup [page :refer [html5 include-js include-css]]))) 4 | 5 | (defn index [] 6 | (html5 7 | [:head 8 | [:meta {:charset "utf-8"}] 9 | [:meta {:http-equiv "X-UA-Compatible" :content "IE=edge"}] 10 | [:meta {:name "viewport" :content "width=device-width, initial-scale=1"}] 11 | [:meta {:name "description" :content "System"}] 12 | [:meta {:name "author" :content "Daniel Szmulewicz"}] 13 | [:title "System"] 14 | 15 | (include-css 16 | "//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" 17 | "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" 18 | "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css") 19 | 20 | ""] 24 | [:body 25 | 26 | [:div#main-area.container 27 | [:p "This is an example project for system."]] 28 | 29 | 30 | (include-js 31 | "https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" 32 | "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js")])) 33 | -------------------------------------------------------------------------------- /examples/boot/src/example/systems.clj: -------------------------------------------------------------------------------- 1 | (ns example.systems 2 | (:require [system.core :refer [defsystem]] 3 | (system.components 4 | [jetty :refer [new-web-server]] 5 | [repl-server :refer [new-repl-server]]) 6 | [environ.core :refer [env]] 7 | [example.handler :refer [app]])) 8 | 9 | (defsystem dev-system 10 | [:web (new-web-server (Integer. (env :http-port)) app)]) 11 | 12 | (defsystem prod-system 13 | [:web (new-web-server (Integer. (env :http-port)) app) 14 | :repl-server (new-repl-server (Integer. (env :repl-port)))]) 15 | -------------------------------------------------------------------------------- /examples/leiningen/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /examples/leiningen/.lein-env: -------------------------------------------------------------------------------- 1 | {:http-port 3000} 2 | -------------------------------------------------------------------------------- /examples/leiningen/LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /examples/leiningen/README.org: -------------------------------------------------------------------------------- 1 | * Example project 2 | 3 | ** What is this? 4 | This project gives you an overview of how you might want to structure a Clojure web app with the [[https://github.com/danielsz/system/][system]] library. 5 | The example project starts a web server in development mode, and a web server + remote repl in production. 6 | ** Instructions 7 | *** Development 8 | Start a repl: 9 | #+BEGIN_SRC bash 10 | $ lein repl 11 | #+END_SRC 12 | 13 | In the user namespace, type: 14 | #+BEGIN_SRC clojure 15 | user=> (start) 16 | #+END_SRC 17 | 18 | Your local web app can be found on [[http://localhost:300][localhost]] on port 3000. 19 | 20 | *** Production 21 | #+BEGIN_SRC sh 22 | lein with-profile prod run 23 | #+END_SRC 24 | Your web app will be found on port 8000, and on port 8001 you will be able to connect to its repl for remote debugging. 25 | -------------------------------------------------------------------------------- /examples/leiningen/dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require 3 | [system.repl :refer [system set-init! start stop reset]] 4 | [example.systems :refer [dev-system]])) 5 | 6 | (set-init! #'dev-system) 7 | ; type (start) in the repl to start your development-time system. 8 | -------------------------------------------------------------------------------- /examples/leiningen/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to example 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /examples/leiningen/project.clj: -------------------------------------------------------------------------------- 1 | (defproject example "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.8.0"] 7 | [ring "1.4.0"] 8 | [ring/ring-defaults "0.1.5"] 9 | [compojure "1.5.0"] 10 | [hiccup "1.0.5"] 11 | [org.danielsz/system "0.4.1"] 12 | [environ "1.0.0"]] 13 | :plugins [[lein-environ "1.0.0"]] 14 | :profiles {:dev {:source-paths ["dev"] 15 | :env {:http-port 3000}} 16 | :prod {:env {:http-port 8000 17 | :repl-port 8001} 18 | :dependencies [[org.clojure/tools.nrepl "0.2.12"]]} 19 | :uberjar {:aot :all}} 20 | :main ^:skip-aot example.core 21 | :target-path "target/%s") 22 | -------------------------------------------------------------------------------- /examples/leiningen/src/example/core.clj: -------------------------------------------------------------------------------- 1 | (ns example.core 2 | (:gen-class) 3 | (:require 4 | [system.repl :refer [set-init! start]] 5 | [example.systems :refer [prod-system]])) 6 | 7 | (defn -main 8 | "Start a production system." 9 | [& args] 10 | (set-init! #'prod-system) 11 | (start)) 12 | -------------------------------------------------------------------------------- /examples/leiningen/src/example/handler.clj: -------------------------------------------------------------------------------- 1 | (ns example.handler 2 | (:require 3 | [compojure.route :as route] 4 | [compojure.core :refer [defroutes GET]] 5 | [ring.middleware.defaults :refer [wrap-defaults site-defaults]] 6 | [example.html :as html])) 7 | 8 | (defroutes routes 9 | (GET "/" [] (html/index)) 10 | (route/not-found (html/index))) 11 | 12 | (def app 13 | (-> routes 14 | (wrap-defaults site-defaults))) 15 | -------------------------------------------------------------------------------- /examples/leiningen/src/example/html.clj: -------------------------------------------------------------------------------- 1 | (ns example.html 2 | (:require 3 | (hiccup [page :refer [html5 include-js include-css]]))) 4 | 5 | (defn index [] 6 | (html5 7 | [:head 8 | [:meta {:charset "utf-8"}] 9 | [:meta {:http-equiv "X-UA-Compatible" :content "IE=edge"}] 10 | [:meta {:name "viewport" :content "width=device-width, initial-scale=1"}] 11 | [:meta {:name "description" :content "System"}] 12 | [:meta {:name "author" :content "Daniel Szmulewicz"}] 13 | [:title "System"] 14 | 15 | (include-css 16 | "//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" 17 | "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" 18 | "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css") 19 | 20 | ""] 24 | [:body 25 | 26 | [:div#main-area.container 27 | [:p "This is an example project for systems."]] 28 | 29 | 30 | (include-js 31 | "https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" 32 | "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js")])) 33 | -------------------------------------------------------------------------------- /examples/leiningen/src/example/systems.clj: -------------------------------------------------------------------------------- 1 | (ns example.systems 2 | (:require [system.core :refer [defsystem]] 3 | (system.components 4 | [jetty :refer [new-web-server]] 5 | [repl-server :refer [new-repl-server]]) 6 | [environ.core :refer [env]] 7 | [example.handler :refer [app]])) 8 | 9 | (defsystem dev-system 10 | [:web (new-web-server (Integer. (env :http-port)) app)]) 11 | 12 | (defsystem prod-system 13 | [:web (new-web-server (Integer. (env :http-port)) app) 14 | :repl-server (new-repl-server (Integer. (env :repl-port)))]) 15 | -------------------------------------------------------------------------------- /examples/leiningen/test/example/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns example.core-test 2 | (:require [clojure.test :refer :all] 3 | [example.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /meyvn.edn: -------------------------------------------------------------------------------- 1 | {:pom {:group-id "org.danielsz", 2 | :artifact-id "system", 3 | :version "0.5.5", 4 | :name "system" 5 | :licenses [{:name "Eclipse Public License -v 1.0" 6 | :url "https://opensource.org/license/epl-1-0/"}]}, 7 | :build-properties {:project-build-sourceEncoding "UTF-8"}, 8 | :packaging {:uberjar {:graalvm {:enabled false, 9 | :with-https false, 10 | :bin-name ""}, 11 | :include-source false, 12 | :excludes {:artifacts ["org.clojure:google-closure-library"], 13 | :filters ["META-INF/*.MF" 14 | "META-INF/*.SF" 15 | "META-INF/*.DSA" 16 | "META-INF/*.RSA"]}, 17 | :appimage {:enabled false, 18 | :source "jpackage-target"}, 19 | :obfuscation {:enabled false}, 20 | :build-properties {:main-class "main.core", 21 | :javafx-class "main.core.App"}, 22 | :pre-compilation-script {:enabled false, 23 | :path "repl/pre-script.clj"}, 24 | :jpackage {:enabled false, 25 | :destination "jpackage-target"}, 26 | :javafx {:enabled false, 27 | :commandline-args "-Dglass.gtk.uiScale=2.0", 28 | :jlink-verbose "true", 29 | :options [], 30 | :gluon-client {:enabled false}, 31 | :jpro {:enabled false}}, 32 | :resources {:css {:enabled false, 33 | :css-source-include "**", 34 | :css-source-exclude "**/*.min.css", 35 | :css-final-file "styles.css", 36 | :web-app-source-dir "resources", 37 | :css-source-dir "css"}, 38 | :cljs {:enabled false, 39 | :compiler-opts {:infer-externs true, 40 | :optimizations :advanced, 41 | :parallel-build true, 42 | :verbose true, 43 | :aot-cache true, 44 | :output-to "js/main.js", 45 | :output-wrapper true, 46 | :foreign-libs [], 47 | :main "main.core"}, 48 | :tools-deps-alias :cljs}, 49 | :images {:enabled false, 50 | :png {:dirs ["resources/images/logo" 51 | "resources/images/testimonials"]}, 52 | :jpeg {:dirs ["resources/images/logo" 53 | "resources/images/testimonials"]}}}, 54 | :enabled false, 55 | :post-compilation-script {:enabled false, 56 | :path "repl/post-script.clj"}}, 57 | :jar {:enabled true, 58 | :gpg {:enabled true}, 59 | :sources {:enabled false}}}, 60 | :testing {:enabled false, :tools-deps-alias :test}, 61 | :scm {:enabled false}, 62 | :interactive {:system {:enabled false, 63 | :var "a.namespace/system-var", 64 | :restart-on-change ["handler.clj" 65 | "system.clj"]}, 66 | :figwheel {:enabled false, 67 | :main {:watch-dirs ["src/cljs"], 68 | :open-url false}, 69 | :build {:main "main.core", 70 | :foreign-libs [], 71 | :output-dir "target/classes/js/compiled", 72 | :output-to "target/classes/js/main.js", 73 | :asset-path "/js/compiled", 74 | :output-wrapper true, 75 | :aot-cache true, 76 | :infer-externs true}, 77 | :tools-deps-alias :figwheel}, 78 | :proxy {:enabled false, 79 | :socks {:host "127.0.0.1", 80 | :port "1080", 81 | :version "5", 82 | :use-system-proxies false}}, 83 | :tools-deps-alias :repl, 84 | :repl-host "127.0.0.1", 85 | :reload-on-save true, 86 | :repl-port :auto, 87 | :enabled false, 88 | :cljs {:enabled false, 89 | :with-repl false, 90 | :compiler-opts {:infer-externs true, 91 | :output-dir "target/classes/js/compiled", 92 | :optimizations :none, 93 | :parallel-build true, 94 | :verbose true, 95 | :aot-cache true, 96 | :output-to "target/classes/js/main.js", 97 | :asset-path "/js/compiled", 98 | :output-wrapper true, 99 | :watch "src/cljs", 100 | :source-map true, 101 | :foreign-libs [], 102 | :main "main.core"}, 103 | :tools-deps-alias :cljs}}, 104 | :profiles {:enabled false} 105 | :distribution-management {:id "clojars", 106 | :url "https://clojars.org/repo"}} 107 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject org.danielsz/system "0.4.7-SNAPSHOT" 2 | :description "Reloaded components à la carte" 3 | :url "https://github.com/danielsz/system" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.10.0" :scope "provided"] 7 | [prismatic/schema "1.1.12"] 8 | [org.danielsz/lang-utils "0.1.3"] 9 | [org.clojure/tools.namespace "1.1.0"] 10 | [io.aviso/pretty "0.1.37"] 11 | [com.stuartsierra/component "1.0.0"]] 12 | :profiles {:test {:dependencies [[org.clojure/clojurescript "1.8.34"] 13 | [nrepl "0.4.5"] 14 | [org.clojure/tools.nrepl "0.2.13"] 15 | [nrepl "0.6.0"] 16 | [ring "1.6.2"] 17 | [ring/ring-defaults "0.3.1"] 18 | [ring/ring-mock "0.3.1"] 19 | [bk/ring-gzip "0.2.1"] 20 | [bidi "2.1.2"] 21 | [im.chit/hara.io.watch "2.1.7"] 22 | [im.chit/hara.io.scheduler "2.3.6"] 23 | [im.chit/adi "0.3.2"] 24 | [com.datomic/datomic-free "0.9.4815.12"] 25 | [com.novemberain/monger "3.1.0"] 26 | [org.clojure/java.jdbc "0.3.5"] 27 | [com.h2database/h2 "1.4.181"] 28 | [org.postgresql/postgresql "9.4-1201-jdbc41"] 29 | [com.novemberain/langohr "2.11.0"] 30 | [clojurewerkz/neocons "3.0.0"] 31 | [clojurewerkz/quartzite "2.0.0"] 32 | [com.taoensso/sente "1.8.1"] 33 | [org.danielsz/etsy "0.1.2"] 34 | [org.danielsz/benjamin "0.1.3"] 35 | [org.danielsz/kampbell "0.1.6"] 36 | [org.danielsz/maarschalk "0.1.3"] 37 | [riemann-clojure-client "0.5.0"] 38 | [io.netty/netty-all "4.1.34.Final"] 39 | [compojure "1.4.0"] 40 | [com.unboundid/unboundid-ldapsdk "4.0.14"] 41 | [http-kit "2.4.0-alpha3"] 42 | [lambdacd "0.13.2"] 43 | [org.immutant/web "2.1.2"] 44 | [hikari-cp "1.6.1"] 45 | [clj-http "3.7.0"] 46 | [javax.xml.bind/jaxb-api "2.3.1"] 47 | [com.taoensso/encore "2.91.0"] 48 | [com.taoensso/carmine "2.16.0"] 49 | [io.replikativ/konserve-carmine "0.1.1"] 50 | [org.elasticsearch/elasticsearch "1.6.0" 51 | :exclusions [org.antlr/antlr-runtime 52 | org.apache.lucene/lucene-analyzers-common 53 | org.apache.lucene/lucene-grouping 54 | org.apache.lucene/lucene-highlighter 55 | org.apache.lucene/lucene-join 56 | org.apache.lucene/lucene-memory 57 | org.apache.lucene/lucene-misc 58 | org.apache.lucene/lucene-queries 59 | org.apache.lucene/lucene-queryparser 60 | org.apache.lucene/lucene-sandbox 61 | org.apache.lucene/lucene-spatial 62 | org.apache.lucene/lucene-suggest 63 | org.ow2.asm/asm 64 | org.ow2.asm/asm-commons]] 65 | [aleph "0.4.0-alpha9"] 66 | [io.replikativ/konserve "0.4.8"] 67 | [factual/durable-queue "0.1.5"]] 68 | :plugins [[lein-cljsbuild "1.1.3"] 69 | [lein-doo "0.1.6"]] 70 | :doo {:build "test"} 71 | :cljsbuild {:builds [{:id "test" 72 | :source-paths ["src" "test"] 73 | :compiler {:output-to "resources/public/js/testable.js" 74 | :main system.cljs-runner 75 | :optimizations :none}}]}} 76 | :doc {:plugins [[lein-codox "0.10.3"]] 77 | :codox {:namespaces [system.components.handler 78 | system.components.middleware 79 | system.components.endpoint 80 | system.components.core-async-pubsub]}}} 81 | :scm {:name "git" 82 | :url "https://github.com/danielsz/system"} 83 | :test-selectors {:default (complement :dependency) 84 | :dependency :dependency}) 85 | -------------------------------------------------------------------------------- /script/pg_test_setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Creates a test user and role for running the PostgreSQL test cases. 4 | # In order to run this, you must be a PostgreSQL superuser. Please 5 | # note that JDBC connections are made over TCP/IP with password 6 | # authentication, so you need to modify pg_hba.conf accordingly. 7 | # 8 | # Usage: 9 | # 10 | # $ su - postgres # Optional, run this as 'postgres' user 11 | # $ script/pg_test_setup.sh 12 | # 13 | # Or: 14 | # $ sudo -u postgres bash pg_test_setup.sh 15 | # 16 | 17 | if [ "$(whoami)" != 'postgres' ]; then 18 | echo "Running this script requires PostgreSQL superuser access. Consider 'su - postgres' to run this as the default PostgreSQL superuser." 19 | fi 20 | 21 | command -v createdb >/dev/null 2>&1 || { 22 | echo >&2 "Cannot find 'createdb', please make sure Postgres is installed and configured" 23 | exit 1 24 | } 25 | 26 | command -v createuser >/dev/null 2>&1 || { 27 | echo >&2 "Cannot find 'createuser', please make sure Postgres is installed and configured" 28 | exit 1 29 | } 30 | 31 | echo "Creating test user..." 32 | createuser system_test_user 33 | 34 | echo "Creating test database..." 35 | createdb system_test_db -O system_test_user 36 | -------------------------------------------------------------------------------- /src/system/boot.clj: -------------------------------------------------------------------------------- 1 | (ns system.boot 2 | {:boot/export-tasks true} 3 | (:require 4 | [system.repl :refer [set-init! start refresh]] 5 | [clojure.tools.namespace.dir :as dir] 6 | [clojure.tools.namespace.track :as track] 7 | [clojure.tools.namespace.find :as ns-find] 8 | [boot.core :as core] 9 | [boot.pod :as pod] 10 | [boot.util :as util] 11 | [clojure.string :as str] 12 | [clojure.java.io :as io])) 13 | 14 | (defn- modified-files [before-fileset after-fileset] 15 | (->> (core/fileset-diff @before-fileset after-fileset) 16 | core/input-files)) 17 | 18 | (defn- restart? [before-fileset after-fileset files {:keys [paths regexes]}] 19 | (let [file-filter (cond paths core/by-path 20 | regexes core/by-re 21 | :else core/by-name) 22 | files (if regexes (map re-pattern files) files)] 23 | (when files (->> (modified-files before-fileset after-fileset) 24 | (file-filter files) 25 | not-empty 26 | boolean)))) 27 | 28 | (defn validate-sys [sys] 29 | (let [dirs (core/get-env :directories) 30 | namespaces (set (mapcat #(ns-find/find-namespaces-in-dir (io/file %)) dirs))] 31 | (cond 32 | (not (var? sys)) (throw (Exception. "sys argument expects a Var, eg. #'system-dev")) 33 | (not (instance? com.stuartsierra.component.SystemMap (try (sys) 34 | (catch Exception e (util/fail (str (.getMessage e) "\n")))))) (throw (Exception. (str sys " is not a SystemMap"))) 35 | (not (contains? namespaces (symbol (str (:ns (meta sys)))))) (throw (Exception. "The System's Var has to be defined in the project's namespaces.")) 36 | :else sys))) 37 | 38 | (defn validate [{:keys [auto files regexes paths]} usage] 39 | (when (and regexes paths) 40 | (util/fail "You can only specify --regexes or --paths, not both.\n") 41 | (throw (Exception. "Task configuration failed."))) 42 | (when (and (not auto) files) 43 | (util/fail "You have specified manual mode, the files vector should not be present in that case.\n") 44 | (throw (Exception. "Task configuration failed.")))) 45 | 46 | (core/deftask system 47 | "Runtime code loading. Automatic System restarts. Fileset driven. 48 | 49 | Mark your namespace with metadata if you don't want to unload it before reloading its definitions. 50 | Valid keys are :passover :blood-of-spring-lamb, :red-pill, :blue-pil or :no-remove-ns. 51 | 52 | You take the blue pill—the story ends. You take the red pill, and I show you how deep the rabbit hole goes." 53 | [s sys SYS edn "The system Var." 54 | a auto bool "Manages the lifecycle of the application automatically." 55 | f files FILES [str] "A vector of files. Will reset the system if a filename in the supplied vector changes." 56 | r regexes bool "Treat --files as regexes, not file names. Only one of regexes|paths is allowed." 57 | p paths bool "Treat --files as classpath paths, not file names. Only one of regexes|paths is allowed." 58 | m mode MODE kw "Standard Lisp mode - recompilation only. Tools.namespace mode - load + unload (default)."] 59 | (validate *opts* *usage*) 60 | (alter-var-root #'clojure.main/repl-requires conj '[system.repl :refer [start go stop reset]]) 61 | (let [fs-prev-state (atom nil) 62 | dirs (into [] (core/get-env :directories)) 63 | tracker (atom (dir/scan-dirs (track/tracker) dirs)) 64 | init-system (if sys 65 | (delay (do (set-init! (validate-sys sys)) 66 | (start) 67 | (util/info (str "Starting " sys "\n")))) 68 | (delay (util/info (str "System was not supplied. Will reload code, but not perform restarts.\n"))))] 69 | (fn [next-task] 70 | (fn [fileset] 71 | (when (and auto (realized? init-system)) 72 | (swap! tracker dir/scan-dirs) 73 | (util/info (str sys ":refreshing\n")) 74 | (refresh tracker {:restart? (restart? fs-prev-state fileset files {:regexes regexes :paths paths}) 75 | :mode (or mode :tools.namespace)})) 76 | @init-system 77 | (next-task (reset! fs-prev-state fileset)))))) 78 | 79 | (core/deftask run 80 | "Run the -main function in some namespace with arguments." 81 | [m main-namespace NAMESPACE str "The namespace containing a -main function to invoke." 82 | a arguments EXPR [edn] "An optional argument sequence to apply to the -main function."] 83 | (core/with-pre-wrap fs 84 | (require (symbol main-namespace) :reload) 85 | (if-let [f (resolve (symbol main-namespace "-main"))] 86 | (apply f arguments) 87 | (throw (ex-info "No -main method found" {:main-namespace main-namespace}))) 88 | fs)) 89 | 90 | -------------------------------------------------------------------------------- /src/system/components/adi.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.adi 2 | (:require [com.stuartsierra.component :as component] 3 | [adi.core :as adi] 4 | [adi.core.types :refer [map->Adi]])) 5 | 6 | (extend-type adi.core.types.Adi 7 | component/Lifecycle 8 | (start [component] 9 | (if (:connection component) 10 | component 11 | (adi/connect! component))) 12 | 13 | (stop [component] 14 | (if (:connection component) 15 | (adi/disconnect! component) 16 | component))) 17 | 18 | (defn new-adi-db 19 | [uri schema & [reset? install-schema?]] 20 | (map->Adi {:meta {:uri uri 21 | :reset? reset? 22 | :install-schema? install-schema?} 23 | :schema schema})) 24 | -------------------------------------------------------------------------------- /src/system/components/aleph.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.aleph 2 | (:require [com.stuartsierra.component :as component] 3 | [aleph.http :refer [start-server]])) 4 | 5 | (defrecord WebServer [port server handler] 6 | component/Lifecycle 7 | (start [component] 8 | (let [handler (get-in component [:handler :handler] handler) 9 | server (start-server handler {:port port :join? false})] 10 | (assoc component :server server))) 11 | (stop [component] 12 | (when server 13 | (.close server) 14 | component))) 15 | 16 | (defn new-web-server 17 | ([port] 18 | (new-web-server port nil)) 19 | ([port handler] 20 | (map->WebServer {:port port :handler handler}))) 21 | -------------------------------------------------------------------------------- /src/system/components/benjamin.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.benjamin 2 | (:require [com.stuartsierra.component :as component] 3 | [benjamin.configuration :as config :refer [set-config!]])) 4 | 5 | (defrecord Logbook [success-fn persistence-fn logbook-fn events persistence-fn-wrap-component? logbook-fn-wrap-component? allow-undeclared-events?] 6 | component/Lifecycle 7 | (start [component] 8 | (if persistence-fn-wrap-component? 9 | (set-config! :persistence-fn (persistence-fn component)) 10 | (set-config! :persistence-fn persistence-fn)) 11 | (if logbook-fn-wrap-component? 12 | (set-config! :logbook-fn (logbook-fn component)) 13 | (set-config! :logbook-fn logbook-fn)) 14 | (when success-fn (set-config! :success-fn success-fn)) 15 | (when events (set-config! :events events)) 16 | (when allow-undeclared-events? (set-config! :allow-undeclared-events? allow-undeclared-events?)) 17 | component) 18 | (stop [component] 19 | (config/reset!) 20 | component)) 21 | 22 | (defn new-logbook [& {:keys [success-fn persistence-fn logbook-fn events persistence-fn-wrap-component? logbook-fn-wrap-component? allow-undeclared-events?] :as options}] 23 | (map->Logbook options)) 24 | 25 | -------------------------------------------------------------------------------- /src/system/components/core_async_pipe.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.core-async-pipe 2 | (:require [clojure.core.async :as async] 3 | [com.stuartsierra.component :as component])) 4 | 5 | 6 | (defn worker 7 | [f to] 8 | (async/thread 9 | (loop [] 10 | (let [v (async/Pipe {:handler handler :options options}))) 49 | 50 | -------------------------------------------------------------------------------- /src/system/components/core_async_pubsub.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.core-async-pubsub 2 | (:require [com.stuartsierra.component :as component] 3 | [clojure.core.async :refer [pub unsub-all close!]])) 4 | 5 | (defrecord PubSub [channel-fn topic-fn buf-fn] 6 | component/Lifecycle 7 | (start [component] 8 | (let [c (channel-fn component) 9 | publication (if buf-fn 10 | (pub c topic-fn buf-fn) 11 | (pub c topic-fn))] 12 | (assoc component :publication publication :channel c))) 13 | (stop [component] 14 | (unsub-all (:publication component)) 15 | (close! (:channel component)) 16 | (dissoc component :publication :channel))) 17 | 18 | (defn new-pubsub 19 | "'channel-fn` is a funtion that receives the component as argument, 20 | so that you are free to implement application-level logic with 21 | dependencies in scope. The function is responsible to create a 22 | channel and to return it. Presumably, you will be writing to the 23 | channel in the body of go blocks or thread constructs. 24 | 25 | 'topic-fn` is the same as in the signature of core.async's 26 | 'sub`. 27 | 28 | Optional 'buf-fn` is the same as in the signature of 29 | core.async's 'sub` 30 | 31 | Please refer to the corresponding test to see a complete example." 32 | ([channel-fn topic-fn] 33 | (new-pubsub channel-fn topic-fn nil)) 34 | ([channel-fn topic-fn buf-fn] 35 | (map->PubSub {:channel-fn channel-fn :topic-fn topic-fn :buf-fn buf-fn}))) 36 | -------------------------------------------------------------------------------- /src/system/components/datomic.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.datomic 2 | (:require [com.stuartsierra.component :as component] 3 | [datomic.api :as d])) 4 | 5 | (defrecord Datomic [uri conn] 6 | component/Lifecycle 7 | (start [component] 8 | (let [db (d/create-database uri) 9 | conn (d/connect uri)] 10 | (assoc component :conn conn))) 11 | (stop [component] 12 | (when conn (d/release conn)) 13 | (assoc component :conn nil))) 14 | 15 | (defn new-datomic-db [uri] 16 | (map->Datomic {:uri uri})) 17 | -------------------------------------------------------------------------------- /src/system/components/datomic_local.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.datomic-local 2 | (:require [com.stuartsierra.component :as component] 3 | [datomic.client.api :as d])) 4 | 5 | (defrecord DatomicLocal [cfg db init-fn] 6 | component/Lifecycle 7 | (start [component] 8 | (let [client (d/client cfg) 9 | _ (d/create-database client db) 10 | conn (d/connect client db)] 11 | (when init-fn 12 | (init-fn conn)) 13 | (assoc component :client client :conn conn))) 14 | (stop [component] 15 | (dissoc component :client :conn))) 16 | 17 | (defn new-datomic-local [& {:keys [cfg db init-fn]}] 18 | (map->DatomicLocal {:cfg cfg :db db :init-fn init-fn})) 19 | -------------------------------------------------------------------------------- /src/system/components/durable_queue.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.durable-queue 2 | (:require [com.stuartsierra.component :as component] 3 | [durable-queue :as q])) 4 | 5 | (defrecord DurableQueue [path opts xs] 6 | component/Lifecycle 7 | (start [component] 8 | (let [queues (q/queues path opts) 9 | guard (volatile! true)] 10 | (doseq [x xs] 11 | (.start (Thread. (fn [] (while @guard 12 | ((:f x) queues component)))))) 13 | (assoc component :queue queues :guard guard))) 14 | (stop [component] 15 | (when-let [guard (:guard component)] 16 | (vswap! guard not)) 17 | (dissoc component :queue :guard))) 18 | 19 | (defn new-durable-queue 20 | "`path' is a directory in the filesystem. 21 | `opts' is the options map supported by durable queues, as documented at 22 | https://github.com/Factual/durable-queue#configuring-the-queues" 23 | [path & {:keys [opts xs] :or {opts {}}}] 24 | (map->DurableQueue {:path path :opts opts :xs xs})) 25 | 26 | -------------------------------------------------------------------------------- /src/system/components/elasticsearch.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.elasticsearch 2 | (:require [com.stuartsierra.component :as component]) 3 | (:import [org.elasticsearch.client.transport TransportClient] 4 | [org.elasticsearch.common.transport InetSocketTransportAddress] 5 | [org.elasticsearch.common.settings ImmutableSettings])) 6 | 7 | (defrecord Elasticsearch [addresses settings client] 8 | component/Lifecycle 9 | (start [component] 10 | (let [builder (.. (ImmutableSettings/settingsBuilder) 11 | (put ^java.util.Map settings)) 12 | client (doto (TransportClient. builder) 13 | (.addTransportAddresses (into-array addresses)))] 14 | (assoc component :client client))) 15 | (stop [component] 16 | (when client 17 | (.close ^TransportClient client)) 18 | (assoc component :client nil))) 19 | 20 | (defn new-elasticsearch-db 21 | ([addresses] 22 | (new-elasticsearch-db addresses {})) 23 | ([addresses settings] 24 | (map->Elasticsearch {:addresses (for [[^String host ^int port] addresses] 25 | (InetSocketTransportAddress. host port)) 26 | :settings settings}))) 27 | -------------------------------------------------------------------------------- /src/system/components/endpoint.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.endpoint 2 | (:require [com.stuartsierra.component :as component] 3 | [reitit.ring :as ring] 4 | [reitit.core :refer [Router]])) 5 | 6 | (defrecord Endpoint [routes middleware] 7 | component/Lifecycle 8 | (start [component] 9 | (assoc component :routes (cond 10 | (vector? routes) (if (not-empty middleware) 11 | (ring/router routes {:data {:middleware middleware}}) 12 | (ring/router routes)) 13 | (and (ifn? routes) (vector? (routes component))) (ring/router (routes component)) 14 | (and (ifn? routes) (satisfies? Router (routes component))) (routes component)))) 15 | (stop [component] 16 | (dissoc component :routes))) 17 | 18 | (defn new-endpoint 19 | "Creates an endpoint. If argument is a vector of route data, will 20 | create reitit routes with optional middleware. If argument is a 21 | function, it will check for presence of Reitit route protocol and 22 | assume that the endpoint is a closure over Reitit routes with 23 | component dependencies in scope." 24 | [& {:keys [routes middleware]}] 25 | (map->Endpoint {:routes routes :middleware middleware})) 26 | -------------------------------------------------------------------------------- /src/system/components/h2.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.h2 2 | (:require [system.components.jdbc :as jdbc] 3 | [com.stuartsierra.component :as component])) 4 | 5 | ;; returns a JDBC component with a H2 spec 6 | 7 | (def DEFAULT-DB-SPEC 8 | {:classname "org.h2.Driver" ; must be in classpath 9 | :subprotocol "h2" 10 | :subname "~/test" 11 | :user "sa" 12 | :password "" 13 | :host "127.0.0.1" 14 | :AUTO_SERVER "TRUE" 15 | :DB_CLOSE_DELAY "-1"}) 16 | 17 | (def DEFAULT-MEM-SPEC 18 | {:classname "org.h2.Driver" ; must be in classpath 19 | :subprotocol "h2" 20 | :subname "mem:test" 21 | :user "sa" 22 | :password "" 23 | :DB_CLOSE_DELAY "-1"}) 24 | 25 | 26 | (defn new-h2-database 27 | ([spec] 28 | (jdbc/new-database spec)) 29 | ([spec init-fn] 30 | (jdbc/new-database spec init-fn))) 31 | -------------------------------------------------------------------------------- /src/system/components/handler.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.handler 2 | (:require [com.stuartsierra.component :as component] 3 | [reitit.core :as r :refer [Router]] 4 | [reitit.ring :as ring] 5 | [clojure.walk :refer [postwalk-replace]])) 6 | 7 | (defn merge-routers [& routers] 8 | (ring/router 9 | (apply merge (map r/routes routers)) 10 | (apply merge (map r/options routers)))) 11 | 12 | (defn endpoints 13 | "Find all endpoints this component depends on, returns map entries of the form 14 | [name component]. An endpoint is its own type." 15 | [component] 16 | (filter #(instance? system.components.endpoint.Endpoint (val %)) component)) 17 | 18 | (defrecord APIHandler [api-route api-prefix api-middleware] 19 | component/Lifecycle 20 | (start [component] 21 | (assoc component 22 | :api-route (api-route component) 23 | :api-middleware (postwalk-replace {:component component} api-middleware))) ;; each api handler can have its own dependencies 24 | (stop [component] 25 | (dissoc component :api-route :api-prefix :api-middleware))) 26 | 27 | (defn api-handlers 28 | "Find all API handlers. An API handler is a siloed handler with a prefix and its own middleware." 29 | [component] 30 | (filter #(instance? APIHandler (val %)) component)) 31 | 32 | (defrecord Handler [default-handler options] 33 | component/Lifecycle 34 | (start [component] 35 | (let [options (if-let [middleware (:middleware options)] 36 | (assoc options :middleware (postwalk-replace {:component component} middleware)) 37 | options) 38 | routes (map :routes (vals (endpoints component))) 39 | routers (apply merge-routers routes) 40 | handler (if-let [api-handlers (api-handlers component)] 41 | (let [site-router (ring/router (r/routes routers) {:data options}) 42 | api-router (for [api-handler (vals api-handlers) 43 | :let [api-routes (:api-route api-handler) 44 | api-middleware (:api-middleware api-handler) 45 | api-prefix (get api-handler :api-prefix "/api") 46 | api-router (cond 47 | (vector? api-routes) (ring/router (conj [api-prefix {:middleware api-middleware}] api-routes)) 48 | (satisfies? Router api-routes) api-routes)]] 49 | (r/routes api-router)) 50 | routers (ring/router (into (r/routes site-router) api-router))] 51 | (ring/ring-handler routers (default-handler component) (dissoc options :middleware))) 52 | (ring/ring-handler routers (default-handler component) options))] 53 | (assoc component :handler handler :debug-routes (r/routes (ring/get-router handler)) :debug-options (r/options (ring/get-router handler))))) 54 | (stop [component] 55 | (dissoc component :handler :debug-routes :debug-options))) 56 | 57 | (defn new-handler 58 | [& {:keys [default-handler options]}] 59 | (map->Handler {:default-handler default-handler :options options})) 60 | 61 | (defn new-api-handler 62 | [& {:keys [api-route api-prefix api-middleware]}] 63 | (map->APIHandler {:api-route api-route :api-prefix api-prefix :api-middleware api-middleware})) 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/system/components/hara_io_scheduler.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.hara-io-scheduler 2 | (:require 3 | [com.stuartsierra.component :as component] 4 | [hara.io.scheduler :as sch])) 5 | 6 | (defrecord Scheduler [scheduler] 7 | component/Lifecycle 8 | (start [component] 9 | (sch/start! scheduler) 10 | (assoc component :scheduler scheduler)) 11 | (stop [component] 12 | (sch/stop! scheduler) 13 | (assoc component :scheduler nil))) 14 | 15 | (defn new-scheduler 16 | ([] 17 | (new-scheduler (sch/scheduler {}))) 18 | ([scheduler] 19 | (map->Scheduler {:scheduler scheduler}))) 20 | -------------------------------------------------------------------------------- /src/system/components/hikari.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.hikari 2 | (:require [com.stuartsierra.component :as component] 3 | [hikari-cp.core :as hikari] 4 | [clojure.java.jdbc :as jdbc])) 5 | 6 | (def DEFAULT-H2-MEM-SPEC 7 | {:adapter "h2" 8 | :url "jdbc:h2:mem:" 9 | :user "sa" 10 | :password ""}) 11 | 12 | ; db-spec format: https://github.com/tomekw/hikari-cp#configuration-options 13 | (defrecord Hikari [db-spec datasource] 14 | component/Lifecycle 15 | (start [component] 16 | (let [s (or datasource (hikari/make-datasource db-spec))] 17 | (assoc component :datasource s))) 18 | (stop [component] 19 | (when datasource 20 | (hikari/close-datasource datasource)) 21 | (assoc component :datasource nil))) 22 | 23 | (defn new-hikari-cp [db-spec] 24 | (map->Hikari {:db-spec db-spec})) 25 | -------------------------------------------------------------------------------- /src/system/components/http_kit.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.http-kit 2 | (:require [com.stuartsierra.component :as component] 3 | [schema.core :as s] 4 | [system.schema :as sc] 5 | [lang-utils.core :refer [seek]] 6 | [org.httpkit.server :refer [run-server]])) 7 | 8 | (defrecord WebServer [options server handler] 9 | component/Lifecycle 10 | (start [component] 11 | (let [handler (if (fn? handler) handler (:handler (val (seek (comp :handler val) component)))) 12 | server (run-server handler options)] 13 | (assoc component :server server))) 14 | (stop [component] 15 | (when server 16 | (server) 17 | component))) 18 | 19 | (def Options 20 | {(s/optional-key :ip) sc/IpAddress 21 | (s/optional-key :port) sc/Port 22 | (s/optional-key :thread) sc/PosInt 23 | (s/optional-key :worker-name-prefix) s/Str 24 | (s/optional-key :queue-size) sc/PosInt 25 | (s/optional-key :max-body) sc/PosInt 26 | (s/optional-key :max-line) sc/PosInt}) 27 | 28 | (defn new-web-server 29 | "Deprecated - this function will eventually be removed. Use keyword arguments instead" 30 | {:deprecated "0.4.1-SNAPSHOT"} 31 | ([port] 32 | (new-web-server port nil {})) 33 | ([port handler] 34 | (new-web-server port handler {})) 35 | ([port handler options] 36 | (map->WebServer {:options (s/validate Options 37 | (merge {:port port} 38 | options)) 39 | :handler handler}))) 40 | 41 | (defn new-http-kit [& {:keys [port handler options]}] 42 | (map->WebServer {:options (s/validate Options (merge {:port port} options)) 43 | :handler handler})) 44 | -------------------------------------------------------------------------------- /src/system/components/immutant_web.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.immutant-web 2 | (:require [schema.core :as s] 3 | [system.schema :as sc] 4 | [com.stuartsierra.component :as component] 5 | [lang-utils.core :refer [seek]] 6 | [immutant.web :refer [run stop]])) 7 | 8 | (defrecord WebServer [handler options] 9 | component/Lifecycle 10 | (start [component] 11 | (let [handler (if (fn? handler) handler (:handler (val (seek (comp :handler val) component)))) 12 | server (run handler options)] 13 | (assoc component :server server))) 14 | (stop [component] 15 | (when-let [server (:server component)] 16 | (stop server)) 17 | (assoc component :server nil))) 18 | 19 | (def Options 20 | {(s/optional-key :host) sc/Hostname 21 | (s/optional-key :port) sc/Port 22 | (s/optional-key :path) s/Str 23 | (s/optional-key :virtual-host) s/Str 24 | (s/optional-key :dispatch?) s/Bool 25 | (s/optional-key :servlet-name) s/Str}) 26 | 27 | (defn new-web-server 28 | "Deprecated - this function will eventually be removed. Use keyword arguments instead" 29 | {:deprecated "0.4.1-SNAPSHOT"} 30 | ([port] 31 | (new-web-server port nil {})) 32 | ([port handler] 33 | (new-web-server port handler {})) 34 | ([port handler options] 35 | (map->WebServer {:options (s/validate Options (merge {:host "0.0.0.0" :port port} options)) 36 | :handler handler}))) 37 | 38 | (defn new-immutant-web [& {:keys [port handler options]}] 39 | (map->WebServer {:options (s/validate Options (merge {:host "0.0.0.0" :port port} options)) 40 | :handler handler})) 41 | -------------------------------------------------------------------------------- /src/system/components/jdbc.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.jdbc 2 | (:require [com.stuartsierra.component :as component] 3 | [clojure.java.jdbc :as jdbc])) 4 | 5 | ;; component for a generic JDBC database 6 | 7 | (defrecord JDBCDatabase [db-spec connection init-fn] 8 | component/Lifecycle 9 | (start [component] 10 | (let [conn (or connection (jdbc/get-connection (:db-spec component))) 11 | _ (when init-fn (init-fn db-spec))] 12 | (assoc component :connection conn))) 13 | (stop [component] 14 | (when-let [conn (:connection component)] 15 | (.close conn)) 16 | (assoc component :connection nil))) 17 | 18 | (defn new-database 19 | ([db-spec] 20 | (map->JDBCDatabase {:db-spec db-spec})) 21 | ([db-spec init-fn] 22 | (map->JDBCDatabase {:db-spec db-spec :init-fn init-fn}))) 23 | -------------------------------------------------------------------------------- /src/system/components/jetty.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.jetty 2 | (:require [schema.core :as s] 3 | [system.schema :as sc] 4 | [com.stuartsierra.component :as component] 5 | [lang-utils.core :refer [seek]] 6 | [ring.adapter.jetty :refer [run-jetty]])) 7 | 8 | (defrecord WebServer [options handler] 9 | component/Lifecycle 10 | (start [component] 11 | (if (:server component) 12 | component 13 | (let [handler (if (fn? handler) handler (:handler (val (seek (comp :handler val) component)))) 14 | server (run-jetty handler options)] 15 | (assoc component :server server)))) 16 | (stop [component] 17 | (if-let [server (:server component)] 18 | (do (.stop server) 19 | (.join server) 20 | (dissoc component :server)) 21 | component))) 22 | 23 | (def Options 24 | {(s/optional-key :configurator) s/Any 25 | (s/optional-key :port) sc/Port 26 | (s/optional-key :host) sc/Hostname 27 | (s/optional-key :join?) s/Bool 28 | (s/optional-key :daemon?) s/Bool 29 | (s/optional-key :ssl?) s/Bool 30 | (s/optional-key :ssl-port) sc/Port 31 | (s/optional-key :keystore) s/Str 32 | (s/optional-key :key-password) s/Str 33 | (s/optional-key :truststore) s/Str 34 | (s/optional-key :trust-password) s/Str 35 | (s/optional-key :max-threads) sc/PosInt 36 | (s/optional-key :min-threads) sc/PosInt 37 | (s/optional-key :max-idle-time) sc/PosInt 38 | (s/optional-key :client-auth) s/Any 39 | (s/optional-key :send-date-header?) s/Bool 40 | (s/optional-key :output-buffer-size) sc/PosInt 41 | (s/optional-key :request-header-size) sc/PosInt 42 | (s/optional-key :response-header-size) sc/PosInt}) 43 | 44 | (defn new-web-server 45 | "Deprecated - this function will eventually be removed. Use keyword arguments instead" 46 | {:deprecated "0.4.1-SNAPSHOT"} 47 | ([port] 48 | (new-web-server port nil {})) 49 | ([port handler] 50 | (new-web-server port handler {})) 51 | ([port handler options] 52 | (map->WebServer {:options (s/validate Options (merge {:port port :join? false} 53 | options)) 54 | :handler handler}))) 55 | 56 | (defn new-jetty [& {:keys [port handler options]}] 57 | (map->WebServer {:options (s/validate Options (merge {:port port :join? false} options)) 58 | :handler handler})) 59 | -------------------------------------------------------------------------------- /src/system/components/kampbell.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.kampbell 2 | (:require [com.stuartsierra.component :as component] 3 | [lang-utils.core :refer [seek]] 4 | [kampbell.core :as k] 5 | [clojure.set :refer [union]])) 6 | 7 | (defrecord Kampbell [entities equality-specs] 8 | component/Lifecycle 9 | (start [component] 10 | (when equality-specs 11 | (alter-var-root #'k/equality-specs union equality-specs)) 12 | (when entities 13 | (when-let [db (seek (comp :store val) component)] 14 | (k/seed-db (:store (val db)) entities))) 15 | component) 16 | (stop [component] 17 | component)) 18 | 19 | (defn new-kampbell 20 | [& {:keys [entities equality-specs]}] 21 | (map->Kampbell {:entities entities :equality-specs equality-specs})) 22 | -------------------------------------------------------------------------------- /src/system/components/konserve.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.konserve 2 | (:require [com.stuartsierra.component :as component] 3 | [konserve.filestore :refer [new-fs-store]] 4 | [konserve.memory :refer [new-mem-store]] 5 | [clojure.core.async :as async :refer [Konserve {:type type :path path :serializer serializer})) 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/system/components/lambdacd.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.lambdacd 2 | (:require [com.stuartsierra.component :as component] 3 | [lambdacd.core :as lambdacd] 4 | [lambdacd.runners :as runners])) 5 | 6 | (defn start-pipelines [pipelines] 7 | (doseq [pipeline pipelines] 8 | (runners/start-one-run-after-another pipeline))) 9 | 10 | (defn stop-pipelines [pipelines] 11 | (doseq [pipeline pipelines] 12 | (when-let [old-ctx (:context pipeline)] 13 | ((get-in old-ctx [:config :shutdown-sequence]) old-ctx)))) 14 | 15 | (defn assemble-pipelines [pipeline-defs config] 16 | (map 17 | (fn [[pname pdef]] 18 | (assoc (lambdacd/assemble-pipeline pdef config) 19 | :name pname)) 20 | pipeline-defs)) 21 | 22 | (defrecord Pipelines [pipeline-defs config] 23 | component/Lifecycle 24 | (start [component] 25 | (let [pipelines (assemble-pipelines pipeline-defs config)] 26 | (start-pipelines pipelines) 27 | (assoc component :pipelines pipelines))) 28 | (stop [component] 29 | (if-let [pipelines (:pipelines component)] 30 | (do (stop-pipelines pipelines) 31 | (dissoc component :pipelines)) 32 | component))) 33 | 34 | (defn new-lambdacd-pipeline [pipeline-defs config] 35 | (map->Pipelines {:pipeline-defs pipeline-defs 36 | :config config})) 37 | -------------------------------------------------------------------------------- /src/system/components/ldap.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.ldap 2 | (:require [com.stuartsierra.component :as component] 3 | [clojure.tools.logging :as log]) 4 | (:import [com.unboundid.ldap.sdk LDAPConnection SimpleBindRequest])) 5 | 6 | (defrecord Ldap [host port bind-dn pass] 7 | component/Lifecycle 8 | (start [component] 9 | (let [conn (LDAPConnection. host port) 10 | bind-request (SimpleBindRequest. bind-dn pass) 11 | bind-result (.bind conn bind-request)] 12 | (when (.hasResponseControl bind-result) (log/warn "Server has response controls to share.")) 13 | (log/debug (.getResultString bind-result)) 14 | (assoc component :conn conn))) 15 | (stop [component] 16 | (.close (:conn component) ) 17 | (assoc component :conn nil))) 18 | 19 | (defn new-ldap [& {:keys [host port bind-dn pass] :or {host "localhost" port 389}}] 20 | (map->Ldap {:host host :port port :bind-dn bind-dn :pass pass})) 21 | -------------------------------------------------------------------------------- /src/system/components/mariadb.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.mariadb 2 | (:require [com.stuartsierra.component :as component]) 3 | (:import [org.mariadb.jdbc MariaDbDataSource])) 4 | 5 | (defrecord MariaDB [jdbc-url init-fn] 6 | component/Lifecycle 7 | (start [component] 8 | (let [data-source (MariaDbDataSource. jdbc-url) 9 | connection (.getConnection data-source)] 10 | (when init-fn (init-fn {:connection connection :data-source data-source})) 11 | (assoc component :connection connection :data-source data-source))) 12 | (stop [component] 13 | (when-let [connection (:connection component)] 14 | (.close connection)) 15 | (assoc component :connection nil :data-source nil))) 16 | 17 | (defn new-mariadb [& {:keys [jdbc-url init-fn]}] 18 | (map->MariaDB {:jdbc-url jdbc-url :init-fn init-fn})) 19 | -------------------------------------------------------------------------------- /src/system/components/mongo.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.mongo 2 | (:require [com.stuartsierra.component :as component] 3 | [schema.core :as s] 4 | [monger.core :as mg] 5 | [monger.credentials :as mcred]) 6 | (:import [com.mongodb MongoOptions ServerAddress])) 7 | 8 | (def Options 9 | {(s/optional-key :connections-per-host) s/Int 10 | (s/optional-key :threads-allowed-to-block-for-connection-multiplier) s/Int 11 | (s/optional-key :max-wait-time) s/Int 12 | (s/optional-key :connect-timeout) s/Int 13 | (s/optional-key :socket-timeout) s/Int 14 | (s/optional-key :socket-keep-alive) s/Bool 15 | (s/optional-key :auto-connect-retry) s/Bool 16 | (s/optional-key :max-auto-connect-retry-time) s/Int}) 17 | 18 | (defrecord Mongo [uri init-fn dbname server-address server-port opts user password conn] 19 | component/Lifecycle 20 | (start [component] 21 | (if conn 22 | component 23 | (cond 24 | opts (let [^MongoOptions opts (mg/mongo-options opts) 25 | ^ServerAddress sa (mg/server-address server-address server-port) 26 | conn (if user 27 | (mg/connect [sa] opts (mcred/create user dbname password)) 28 | (mg/connect sa opts)) 29 | db (mg/get-db conn dbname) 30 | _ (when init-fn (init-fn db))] 31 | (assoc component :db db :conn conn)) 32 | uri (let [{:keys [conn db]} (mg/connect-via-uri uri) 33 | _ (when init-fn (init-fn db))] 34 | (assoc component :db db :conn conn)) 35 | :else (let [conn (mg/connect) 36 | db (mg/get-db conn "mongo-dev")] 37 | (assoc component :db db :conn conn))))) 38 | 39 | (stop [component] 40 | (when conn (try (mg/disconnect conn) 41 | (catch Throwable t (println t "Error when stopping Mongo component")))) 42 | (-> component 43 | (dissoc :db) 44 | (assoc :conn nil)))) 45 | 46 | (defn new-mongo-db 47 | ([] 48 | (map->Mongo {})) 49 | ([uri] 50 | (map->Mongo {:uri uri})) 51 | ([uri init-fn] 52 | (map->Mongo {:uri uri :init-fn init-fn})) 53 | ([server-address server-port dbname opts] 54 | (map->Mongo {:server-address server-address 55 | :server-port server-port 56 | :dbname dbname 57 | :opts (s/validate Options opts)})) 58 | ([server-address server-port dbname opts user password] 59 | (map->Mongo {:server-address server-address 60 | :server-port server-port 61 | :dbname dbname 62 | :opts (s/validate Options opts) 63 | :user user 64 | :password password})) 65 | ([server-address server-port dbname opts init-fn] 66 | (map->Mongo {:server-address server-address 67 | :server-port server-port 68 | :dbname dbname 69 | :opts (s/validate Options opts) 70 | :init-fn init-fn})) 71 | ([server-address server-port dbname opts init-fn user password] 72 | (map->Mongo {:server-address server-address 73 | :server-port server-port 74 | :dbname dbname 75 | :opts (s/validate Options opts) 76 | :init-fn init-fn 77 | :user user 78 | :password password}))) 79 | -------------------------------------------------------------------------------- /src/system/components/neo4j.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.neo4j 2 | (:require [com.stuartsierra.component :as component] 3 | [clojurewerkz.neocons.rest :as n])) 4 | 5 | (defrecord Neo4j [uri user passwd conn] 6 | component/Lifecycle 7 | (start [component] 8 | (cond 9 | (and uri user passwd) (assoc component :conn (n/connect uri user passwd)) 10 | uri (assoc component :conn (n/connect uri)))) 11 | (stop [component] 12 | (assoc component :conn nil))) 13 | 14 | (defn new-neo4j-db 15 | ([uri] 16 | (map->Neo4j {:uri uri})) 17 | ([uri user passwd] 18 | (map->Neo4j {:uri uri :user user :passwd passwd}))) 19 | -------------------------------------------------------------------------------- /src/system/components/next_jdbc.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.next-jdbc 2 | (:require [com.stuartsierra.component :as component] 3 | [next.jdbc :as jdbc])) 4 | 5 | (defrecord NextJDBCDatabase [db-spec jdbc-url] 6 | component/Lifecycle 7 | (start [component] 8 | (let [datasource (if jdbc-url 9 | (jdbc/get-datasource jdbc-url) 10 | (jdbc/get-datasource db-spec))] 11 | (assoc component :datasource datasource))) 12 | (stop [component] 13 | (assoc component :datasource nil))) 14 | 15 | (defn new-next-jdbc [& {:keys [db-spec jdbc-url]}] 16 | (map->NextJDBCDatabase {:db-spec db-spec :jdbc-url jdbc-url})) 17 | -------------------------------------------------------------------------------- /src/system/components/poco.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.poco 2 | (:require [com.stuartsierra.component :as component])) 3 | 4 | (defrecord Poco [db] 5 | component/Lifecycle 6 | (start [component] 7 | component) 8 | (stop [component] 9 | (assoc component :db nil))) 10 | 11 | (defn new-poco [xs] 12 | (map->Poco {:db xs})) 13 | 14 | -------------------------------------------------------------------------------- /src/system/components/postgres.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.postgres 2 | (:require [system.components.jdbc :as data])) 3 | 4 | ;; component for a PostgreSQL database 5 | 6 | (def DEFAULT-DB-SPEC 7 | {:classname "org.postgresql.Driver" ; must be in classpath 8 | :subprotocol "postgresql" 9 | :subname "" 10 | :user "test" 11 | :password "test" 12 | :host "127.0.0.1"}) 13 | 14 | (defn new-postgres-database 15 | [spec] 16 | (data/new-database spec)) 17 | -------------------------------------------------------------------------------- /src/system/components/quartzite.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.quartzite 2 | (:require 3 | [com.stuartsierra.component :as component] 4 | [clojurewerkz.quartzite.scheduler :as qs])) 5 | 6 | (defrecord Scheduler [scheduler] 7 | component/Lifecycle 8 | (start [component] 9 | (let [s (-> (qs/initialize) qs/start)] 10 | (assoc component :scheduler s))) 11 | (stop [component] 12 | (qs/shutdown scheduler) 13 | component)) 14 | 15 | (defn new-scheduler 16 | [] 17 | (map->Scheduler {})) 18 | -------------------------------------------------------------------------------- /src/system/components/rabbitmq.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.rabbitmq 2 | (:require [com.stuartsierra.component :as component] 3 | [langohr.core :as rmq] 4 | [langohr.channel :as lch])) 5 | 6 | (defrecord Rabbit [uri conn ch] 7 | component/Lifecycle 8 | (start [component] 9 | (let [conn (rmq/connect {:uri uri}) 10 | ch (lch/open conn)] 11 | (assoc component :conn conn :ch ch))) 12 | (stop [component] 13 | (try (rmq/close ch) 14 | (catch com.rabbitmq.client.AlreadyClosedException e nil)) 15 | (try (rmq/close conn) 16 | (catch com.rabbitmq.client.AlreadyClosedException e nil)) 17 | component)) 18 | 19 | (defn new-rabbit-mq [uri] 20 | (map->Rabbit {:uri uri})) 21 | -------------------------------------------------------------------------------- /src/system/components/redis.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.redis 2 | (:require [com.stuartsierra.component :as component])) 3 | 4 | (defrecord Redis [pool spec] 5 | component/Lifecycle 6 | (start [component] 7 | (let [server-conn {:pool pool :spec spec}] 8 | (assoc component :server-conn server-conn))) 9 | (stop [component] 10 | (assoc component :server-conn nil))) 11 | 12 | (defn new-redis [& {:keys [pool spec] :or {pool {} spec {}}}] 13 | (map->Redis {:pool pool :spec spec})) 14 | -------------------------------------------------------------------------------- /src/system/components/redis_pubsub.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.redis-pubsub 2 | (:require [com.stuartsierra.component :as component] 3 | [taoensso.carmine :as car])) 4 | 5 | (defrecord RedisPubSub [host port topic handler options] 6 | component/Lifecycle 7 | (start [component] 8 | (if (:listener component) 9 | component 10 | (let [conn {:pool {} :spec {:host host :port port}} 11 | handler (if (:wrap-component? options) 12 | (handler component) 13 | handler) 14 | listener (car/with-new-pubsub-listener (:spec conn) 15 | {topic handler} 16 | (car/psubscribe topic))] 17 | (assoc component :listener listener)))) 18 | (stop [component] 19 | (if-let [listener (:listener component)] 20 | (do (car/close-listener listener) 21 | (dissoc component :listener)) 22 | component))) 23 | 24 | (defn new-redis-pubsub 25 | ([topic handler] 26 | (new-redis-pubsub "127.0.0.1" 6379 topic handler {})) 27 | ([topic handler options] 28 | (new-redis-pubsub "127.0.0.1" 6379 topic handler options)) 29 | ([host port topic handler options] 30 | (map->RedisPubSub {:host host :port port :topic topic :handler handler :options options}))) 31 | -------------------------------------------------------------------------------- /src/system/components/redis_queue.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.redis-queue 2 | (:require [com.stuartsierra.component :as component] 3 | [taoensso.carmine.message-queue :as car-mq])) 4 | 5 | (defrecord RedisQueue [pool spec xs] 6 | component/Lifecycle 7 | (start [component] 8 | (let [conn {:pool pool :spec spec} 9 | workers (doall (for [x xs] 10 | (car-mq/worker conn (:q x) {:handler ((:f x) component)})))] 11 | (assoc component :workers workers :conn conn))) 12 | (stop [component] 13 | (doseq [worker (:workers component)] 14 | (car-mq/stop worker)) 15 | (assoc component :conn nil :workers nil))) 16 | 17 | (defn new-redis-queue [& {:keys [xs pool spec] :or {pool {} spec {}}}] 18 | (map->RedisQueue {:xs xs :pool pool :spec spec})) 19 | 20 | -------------------------------------------------------------------------------- /src/system/components/repl_server.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.repl-server 2 | (:require [com.stuartsierra.component :as component])) 3 | 4 | 5 | (defrecord ReplServer [port bind with-cider] 6 | component/Lifecycle 7 | (start [component] 8 | (let [start-server (or (resolve 'nrepl.server/start-server) 9 | (resolve 'clojure.tools.nrepl.server/start-server)) 10 | nrepl-handler #(do (require 'cider.nrepl) 11 | (ns-resolve 'cider.nrepl 'cider-nrepl-handler)) 12 | handler (when with-cider (nrepl-handler))] 13 | (assoc component :server (start-server :port port :bind bind :handler handler)))) 14 | (stop [{server :server :as component}] 15 | (when server 16 | (let [stop-server (or (resolve 'nrepl.server/stop-server) 17 | (resolve 'clojure.tools.nrepl.server/stop-server))] 18 | (stop-server server) 19 | component)))) 20 | 21 | (defn new-repl-server 22 | [& {:keys [port bind with-cider] :or {bind "localhost" with-cider false}}] 23 | (try 24 | (require 'nrepl.server) 25 | (catch java.io.FileNotFoundException e 26 | (require 'clojure.tools.nrepl.server))) 27 | (map->ReplServer {:port port :bind bind :with-cider with-cider})) 28 | -------------------------------------------------------------------------------- /src/system/components/riemann_client.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.riemann-client 2 | (:require [com.stuartsierra.component :as component] 3 | [riemann.client :as r] 4 | [clojure.tools.logging :as log]) 5 | (:import [io.riemann.riemann.client OverloadedException])) 6 | 7 | (defrecord RiemannClient [host port transport] 8 | component/Lifecycle 9 | (start [component] 10 | (let [client (case transport 11 | :tcp (r/tcp-client {:host host :port port}) 12 | :udp (r/udp-client {:host host :port port})) 13 | a (-> (agent {}) 14 | (add-watch :key (fn [_k _r _os ns] (try (deref (:promise ns) 5000 ::timeout) 15 | (catch Exception e (log/error (.getMessage e))))))) 16 | f (fn [state client event] 17 | (let [v (try 18 | (r/send-event client event) 19 | (catch OverloadedException e (log/error (.getMessage e))))] 20 | (assoc state :promise v)))] 21 | (assoc component :client client :send-fn (partial send a f client)))) 22 | (stop [component] 23 | (if-let [client (:client component)] 24 | (assoc component :client (r/close! client)) 25 | component))) 26 | 27 | (defn new-riemann-client 28 | "Returns a Riemann client. 29 | 30 | `send-fn` is a function that accepts a Riemann struct, which it will 31 | send in an agent threadpool (asynchronously). The promise that the 32 | Riemann `send-event` returns will be derefed in a watcher (also on 33 | the threadpool), and will log errors if any occur." 34 | [& {:keys [host port transport] :or {host "127.0.0.1" port 5555 transport :tcp}}] 35 | (map->RiemannClient {:host host :port port :transport transport})) 36 | -------------------------------------------------------------------------------- /src/system/components/rj9.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.rj9 2 | (:require [ring.adapter.jetty9 :refer [run-jetty]] 3 | [com.stuartsierra.component :as component])) 4 | 5 | (defrecord WebServer [handler options] 6 | component/Lifecycle 7 | (start [component] 8 | (let [handler (if (fn? handler) 9 | handler 10 | (get-in component [:handler :handler])) 11 | server (run-jetty handler options)] 12 | (assoc component :server server))) 13 | (stop [component] 14 | (when-let [server (:server component)] 15 | (.stop server)) 16 | (assoc component :server nil))) 17 | 18 | (defn new-rj9 [& {:keys [port handler options]}] 19 | (map->WebServer {:options (merge {:host "0.0.0.0" :port port :join? false} options) 20 | :handler handler})) 21 | -------------------------------------------------------------------------------- /src/system/components/scheduled_executor_service.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.scheduled-executor-service 2 | (:require 3 | [com.stuartsierra.component :as component]) 4 | (:import [java.util.concurrent ScheduledThreadPoolExecutor])) 5 | 6 | (defrecord Scheduler [n-threads xs] 7 | component/Lifecycle 8 | (start [component] 9 | (let [s (ScheduledThreadPoolExecutor. n-threads)] 10 | (doseq [x xs] 11 | (case (:method x) 12 | :fixed-delay (.scheduleWithFixedDelay ^ScheduledThreadPoolExecutor s ((:f x) component) (:initial-delay x) (:period x) (:unit x)) 13 | :fixed-rate (.scheduleAtFixedRate ^ScheduledThreadPoolExecutor s ((:f x) component) (:initial-delay x) (:period x) (:unit x)) 14 | :one-off (.schedule ^ScheduledThreadPoolExecutor s ((:f x) (assoc component :s s)) (:initial-delay x) (:unit x)))) 15 | (assoc component :scheduler s))) 16 | (stop [component] 17 | (when-let [scheduler (:scheduler component)] 18 | (.shutdown scheduler)) 19 | component)) 20 | 21 | (defn new-scheduler [& {:keys [n-threads xs] :or {n-threads (.availableProcessors (Runtime/getRuntime))}}] 22 | (map->Scheduler {:n-threads n-threads :xs xs})) 23 | -------------------------------------------------------------------------------- /src/system/components/sente.cljc: -------------------------------------------------------------------------------- 1 | (ns system.components.sente 2 | #?(:clj 3 | (:require [com.stuartsierra.component :as component] 4 | [reitit.ring :as r] 5 | [taoensso.sente :as sente] 6 | [ring.util.response :as ring] 7 | [clojure.tools.logging :as log]) 8 | :cljs 9 | (:require [com.stuartsierra.component :as component] 10 | [taoensso.sente :as sente]))) 11 | 12 | 13 | #?(:clj 14 | (defn sente-routes [{{ring-ajax-post :ring-ajax-post ring-ajax-get-or-ws-handshake :ring-ajax-get-or-ws-handshake middleware :middleware} :sente}] 15 | (r/router ["/chsk" (cond-> {:get (fn [req] (try 16 | (ring-ajax-get-or-ws-handshake req) 17 | (catch clojure.lang.ExceptionInfo e 18 | (log/error (ex-data e)) 19 | (-> (ring/response (.getMessage e)) 20 | (ring/status 400))))) 21 | :post (fn [req] (ring-ajax-post req))} 22 | (some? middleware) (assoc :middleware middleware))]))) 23 | 24 | 25 | ;; Sente supports both CLJ and CLJS as a server 26 | (defrecord ChannelSocketServer [handler adapter options middleware] 27 | component/Lifecycle 28 | (start [component] 29 | (let [{:keys [ch-recv ajax-post-fn ajax-get-or-ws-handshake-fn send-fn connected-uids]} (sente/make-channel-socket-server! adapter options)] 30 | (assoc component 31 | :ch-chsk ch-recv 32 | :ring-ajax-post ajax-post-fn 33 | :ring-ajax-get-or-ws-handshake ajax-get-or-ws-handshake-fn 34 | :chsk-send! send-fn 35 | :connected-uids connected-uids 36 | :router (sente/start-chsk-router! ch-recv (handler component))))) 37 | (stop [component] 38 | (when-let [stop-f (:router component)] 39 | (stop-f)) 40 | component)) 41 | 42 | (defn new-channel-socket-server [& {:keys [handler adapter options middleware]}] 43 | (map->ChannelSocketServer {:handler handler 44 | :adapter adapter 45 | :options options 46 | :middleware middleware})) 47 | 48 | ;; Sente does not support CLJ as a client yet 49 | #?(:cljs 50 | (defrecord ChannelSocketClient [path csrf-token options] 51 | component/Lifecycle 52 | (start [component] 53 | (let [{:keys [chsk ch-recv send-fn state]} (sente/make-channel-socket-client! path csrf-token options)] 54 | (assoc component 55 | :chsk chsk 56 | :ch-chsk ch-recv ; ChannelSocket's receive channel 57 | :chsk-send! send-fn ; ChannelSocket's send API fn 58 | :chsk-state state))) 59 | (stop [component] 60 | (when-let [chsk (:chsk component)] 61 | (sente/chsk-disconnect! chsk)) 62 | (when-let [stop-f (:router component)] 63 | (stop-f)) 64 | (assoc component 65 | :router nil 66 | :chsk nil 67 | :ch-chsk nil 68 | :chsk-send! nil 69 | :chsk-state nil)))) 70 | 71 | #?(:cljs 72 | (defn new-channel-socket-client 73 | ([csrf-token] 74 | (new-channel-socket-client "/chsk" csrf-token)) 75 | ([path csrf-token] 76 | (new-channel-socket-client path csrf-token {:type :auto})) 77 | ([path csrf-token options] 78 | (map->ChannelSocketClient {:path path 79 | :csrf-token csrf-token 80 | :options options})))) 81 | -------------------------------------------------------------------------------- /src/system/components/undertow.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.undertow 2 | (:require [ring.adapter.undertow :refer [run-undertow]] 3 | [com.stuartsierra.component :as component])) 4 | 5 | (defrecord WebServer [handler options] 6 | component/Lifecycle 7 | (start [component] 8 | (let [handler (if (fn? handler) 9 | handler 10 | (get-in component [:handler :handler])) 11 | server (run-undertow handler options)] 12 | (assoc component :server server))) 13 | (stop [component] 14 | (when-let [server (:server component)] 15 | (.stop server)) 16 | (assoc component :server nil))) 17 | 18 | (defn new-undertow [& {:keys [port handler options]}] 19 | (map->WebServer {:options (merge {:host "0.0.0.0" :port port} options) 20 | :handler handler})) 21 | -------------------------------------------------------------------------------- /src/system/components/watcher.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.watcher 2 | (:require [com.stuartsierra.component :as component] 3 | [hara.io.watch :as watch] 4 | [system.monitoring watcher 5 | [core :refer [started? stopped?]]]) 6 | (:import hara.io.watch.Watcher)) 7 | 8 | (extend-protocol component/Lifecycle 9 | Watcher 10 | (component/start [watcher] 11 | (if (stopped? watcher) 12 | (watch/start-watcher watcher) 13 | watcher)) 14 | (component/stop [watcher] 15 | (if (started? watcher) 16 | (watch/stop-watcher watcher) 17 | watcher))) 18 | 19 | (defn new-watcher [paths callback & [config]] 20 | (watch/watcher paths callback config)) 21 | -------------------------------------------------------------------------------- /src/system/core.clj: -------------------------------------------------------------------------------- 1 | (ns system.core 2 | (:require 3 | [com.stuartsierra.component :as component])) 4 | 5 | (defmacro defsystem 6 | "Convenience macro to build a system" 7 | [fname system-map] 8 | `(defn ~fname [] (component/system-map ~@system-map))) 9 | -------------------------------------------------------------------------------- /src/system/monitoring/core.clj: -------------------------------------------------------------------------------- 1 | (ns system.monitoring.core 2 | "Monitoring API. This namespace defines the `Monitoring' protocol 3 | and extends it for components that have an API to query the status of 4 | the service") 5 | 6 | (defprotocol Monitoring 7 | "Protocol defining a single function, `status', that different 8 | components can use to expose the current status." 9 | (started? [component] "query if component is started?") 10 | (stopped? [component] "query if component is stopped?")) 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/system/monitoring/jetty.clj: -------------------------------------------------------------------------------- 1 | (ns system.monitoring.jetty 2 | (:require [system.monitoring.core :as c]) 3 | (:import [system.components.jetty WebServer])) 4 | 5 | (extend-type WebServer 6 | c/Monitoring 7 | (started? [component] 8 | (.isStarted (:server component))) 9 | (stopped? [component] 10 | (.isStopped (:server component)))) 11 | -------------------------------------------------------------------------------- /src/system/monitoring/quartzite.clj: -------------------------------------------------------------------------------- 1 | (ns system.monitoring.quartzite 2 | (:require [system.monitoring.core :as c] 3 | [clojurewerkz.quartzite.scheduler :as s]) 4 | (:import [system.components.quartzite Scheduler])) 5 | 6 | (extend-type Scheduler 7 | c/Monitoring 8 | (started? [component] 9 | (s/started? (:scheduler component))) 10 | (stopped? [component] 11 | (s/shutdown? (:scheduler component)))) 12 | -------------------------------------------------------------------------------- /src/system/monitoring/riemann_client.clj: -------------------------------------------------------------------------------- 1 | (ns system.monitoring.riemann-client 2 | (:require [system.monitoring.core :as c] 3 | [riemann.client :as r]) 4 | (:import [system.components.riemann_client RiemannClient])) 5 | 6 | (extend-type RiemannClient 7 | c/Monitoring 8 | (started? [component] 9 | (r/connected? (:client component))) 10 | (stopped? [component] 11 | (not (r/connected? (:client component))))) 12 | -------------------------------------------------------------------------------- /src/system/monitoring/scheduled_executor_service.clj: -------------------------------------------------------------------------------- 1 | (ns system.monitoring.scheduled-executor-service 2 | (:require [system.monitoring.core :as c]) 3 | (:import [system.components.scheduled_executor_service Scheduler])) 4 | 5 | (extend-type Scheduler 6 | c/Monitoring 7 | (started? [component] 8 | (not (.isShutdown (:scheduler component)))) 9 | (stopped? [component] 10 | (.isShutdown (:scheduler component)))) 11 | -------------------------------------------------------------------------------- /src/system/monitoring/watcher.clj: -------------------------------------------------------------------------------- 1 | (ns system.monitoring.watcher 2 | (:require [system.monitoring.core :as c]) 3 | (:import hara.io.watch.Watcher)) 4 | 5 | (extend-protocol c/Monitoring 6 | Watcher 7 | (started? [component] 8 | (contains? component :running)) 9 | (stopped? [component] 10 | (not (contains? component :running)))) 11 | -------------------------------------------------------------------------------- /src/system/nrepl_middleware.clj: -------------------------------------------------------------------------------- 1 | (ns system.nrepl-middleware 2 | (:require [nrepl.misc :refer [response-for]] 3 | [nrepl.middleware :refer [set-descriptor!]] 4 | [nrepl.middleware.session :refer [session]] 5 | [nrepl.transport :as t] 6 | [clojure.edn :as edn] 7 | [clojure.string :as str] 8 | [clojure.java.io :as io] 9 | [system.repl :refer [go reset set-init!]]) 10 | (:import [java.util.jar JarInputStream])) 11 | 12 | (defn meyvn-classpath [] 13 | (let [url (-> (System/getProperty "java.class.path") 14 | io/as-file 15 | io/as-url) 16 | is (JarInputStream. (.openStream url)) 17 | mf (.getManifest is)] 18 | (.getValue (.getMainAttributes mf) "Class-Path"))) 19 | 20 | (defn key-to-java-property [k] 21 | (-> k 22 | name 23 | (str/replace "-" "."))) 24 | 25 | (defn set-profile-properties [xs] 26 | (doseq [[k v] xs 27 | :let [k (key-to-java-property k)]] 28 | (System/setProperty k v))) 29 | 30 | (defn read-conf [] 31 | (-> "meyvn.edn" 32 | slurp 33 | edn/read-string)) 34 | 35 | (defn system-init [] 36 | (let [conf (read-conf) 37 | profile (get-in conf [:profiles :development]) 38 | sys (symbol (get-in conf [:interactive :system :var]))] 39 | (set-profile-properties profile) 40 | (require (symbol (namespace sys))) 41 | (set-init! (resolve sys)))) 42 | 43 | (defn wrap-system 44 | [h] 45 | (fn [{:keys [op transport] :as msg}] 46 | (condp = op 47 | "meyvn-properties" (let [conf (read-conf) 48 | profile (get-in conf [:profiles :development])] 49 | (set-profile-properties profile) 50 | (t/send transport (response-for msg :status :done :report {:count (count (keys profile))}))) 51 | "meyvn-system-init" (do (system-init) 52 | (t/send transport (response-for msg :status :done :value system.repl/system-sym))) 53 | "meyvn-system-go" (do (when-not (bound? (var system.repl/system-sym)) 54 | (system-init)) 55 | (go) 56 | (t/send transport (response-for msg :status :done :value "OK"))) 57 | "meyvn-system-reset" (do (when-not (bound? (var system.repl/system-sym)) 58 | (system-init)) 59 | (reset) 60 | (t/send transport (response-for msg :status :done :value "OK"))) 61 | "meyvn-classpath" (t/send transport (response-for msg :status :done :value (meyvn-classpath))) 62 | (h msg)))) 63 | 64 | 65 | (set-descriptor! #'wrap-system 66 | {:requires #{#'session} 67 | :handles {"meyvn-system-init" {:doc "Sets the system var"} 68 | "meyvn-system-go" {:doc "Starts the system"} 69 | "meyvn-system-reset" {:doc "Resets the system"} 70 | "meyvn-properties" {:doc "Sets properties for the environment"} 71 | "meyvn-classpath" {:doc "Returns classpath"}}}) 72 | 73 | -------------------------------------------------------------------------------- /src/system/reload.clj: -------------------------------------------------------------------------------- 1 | (ns system.reload 2 | (:require [clojure.tools.namespace.track :as track])) 3 | 4 | (defn clean-lib 5 | "Remove lib's mappings (unintern symbols)" 6 | [lib] 7 | (when (find-ns lib) 8 | (doseq [[sym _] (ns-publics lib)] 9 | (when (ns-resolve lib sym) 10 | (println "removing" sym "from" lib) 11 | (ns-unmap lib sym))) 12 | (doseq [[sym _] (ns-aliases lib)] 13 | (ns-unalias lib sym)))) 14 | 15 | (defn remove-lib 16 | "Remove lib's namespace and remove lib from the set of loaded libs." 17 | [lib] 18 | (if-not (some #{:passover :blood-of-spring-lamb :red-pill :blue-pill :skip-remove-ns :פסח} (keys (meta (find-ns lib)))) 19 | (do (remove-ns lib) 20 | (dosync (alter @#'clojure.core/*loaded-libs* disj lib))) 21 | (do (doseq [[sym _] (ns-aliases lib)] 22 | (ns-unalias lib sym)) 23 | (println "Passing over" lib)))) 24 | 25 | (defn track-reload-one 26 | "Executes the next pending unload/reload operation in the dependency 27 | tracker. Returns the updated dependency tracker. If reloading caused 28 | an error, it is captured as ::error and the namespace which caused 29 | the error is ::error-ns." 30 | [tracker unload?] 31 | (let [{unload ::track/unload, load ::track/load} tracker] 32 | (cond 33 | (seq unload) (let [n (first unload)] 34 | (when unload? (remove-lib n)) 35 | (update-in tracker [::track/unload] rest)) 36 | (seq load) (let [n (first load)] 37 | (try 38 | (require n :reload) 39 | (update-in tracker [::track/load] rest) 40 | (catch Throwable t 41 | (assoc tracker 42 | ::error t ::error-ns n ::track/unload load)))) 43 | :else 44 | tracker))) 45 | 46 | (defn track-reload 47 | "Executes all pending unload/reload operations on dependency tracker 48 | until either an error is encountered or there are no more pending 49 | operations." 50 | [tracker unload?] 51 | (loop [tracker (dissoc tracker ::error ::error-ns)] 52 | (let [{error ::error unload ::track/unload load ::track/load} tracker] 53 | (if (and (not error) 54 | (or (seq load) (seq unload))) 55 | (recur (track-reload-one tracker unload?)) 56 | tracker)))) 57 | -------------------------------------------------------------------------------- /src/system/repl.clj: -------------------------------------------------------------------------------- 1 | (ns system.repl 2 | (:require [com.stuartsierra.component :as component] 3 | [clojure.tools.namespace.track :as track] 4 | [system.reload :as reload] 5 | [clojure.stacktrace :as st] 6 | [io.aviso.ansi :refer [bright-red bright-yellow]] )) 7 | 8 | 9 | (declare system) 10 | (declare system-sym) 11 | 12 | (defn set-init! [sys] 13 | (intern 'system.repl 'system-sym (symbol (str (:ns (meta sys))) (str (:name (meta sys)))))) 14 | 15 | (defn init 16 | "Constructs the current development system." 17 | [] 18 | (alter-var-root #'system (constantly ((find-var system-sym))))) 19 | 20 | (defn start 21 | "Starts the current development system." 22 | [] 23 | (init) 24 | (alter-var-root #'system component/start)) 25 | 26 | (defn stop 27 | "Shuts down and destroys the current development system." 28 | [] 29 | (alter-var-root #'system 30 | (fn [s] (when s (component/stop s))))) 31 | 32 | (defn reset [] 33 | (stop) 34 | (start)) 35 | 36 | (defn refresh [tracker {:keys [restart? mode]}] 37 | (when restart? 38 | (stop) 39 | (println (bright-yellow (str "Stopping " system-sym)))) 40 | 41 | (when (= mode :tools.namespace) (println "Unmapping namespaces:" (::track/unload @tracker))) 42 | (println "Recompiling namespaces:" (::track/load @tracker)) 43 | (swap! tracker reload/track-reload (= mode :tools.namespace)) 44 | (when (::reload/error @tracker) 45 | (swap! @(resolve 'boot.core/*warnings*) inc) 46 | (println (bright-red (str "Error reloading: " (::reload/error-ns @tracker)))) 47 | (st/print-throwable (::reload/error @tracker))) 48 | 49 | (when restart? 50 | (start) 51 | (println (bright-yellow (str "Starting " system-sym))))) 52 | 53 | 54 | ;; No need to break API signatures 55 | 56 | (def go start) 57 | 58 | -------------------------------------------------------------------------------- /src/system/schema.clj: -------------------------------------------------------------------------------- 1 | (ns system.schema 2 | (:require [schema.core :as s])) 3 | 4 | (def PosInt 5 | (s/both s/Int (s/pred pos?))) 6 | 7 | (def Port 8 | (s/both s/Int (s/pred #(<= 0 % 65535)))) 9 | 10 | (def IpAddress 11 | s/Str) 12 | 13 | (def Hostname 14 | s/Str) 15 | 16 | (def FilePath 17 | s/Str) 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/system/cljs_runner.cljs: -------------------------------------------------------------------------------- 1 | (ns system.cljs-runner 2 | (:require [cljs.test :as test] 3 | [doo.runner :refer-macros [doo-all-tests doo-tests]] 4 | [system.components.sente-client-test])) 5 | 6 | (doo-tests 'system.components.sente-client-test) 7 | -------------------------------------------------------------------------------- /test/system/components/adi_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.adi-test 2 | (:require [adi.core :as adi] 3 | [system.components.adi :refer [new-adi-db]] 4 | [com.stuartsierra.component :as component] 5 | [clojure.test :refer [deftest is]])) 6 | 7 | (def db-schema {:person/name [{:type :string}]}) 8 | (def adi-db (new-adi-db "datomic:mem://system-adi-test" db-schema true true)) 9 | 10 | (deftest start-adi 11 | (alter-var-root #'adi-db component/start) 12 | (is (= (type (:connection adi-db)) datomic.peer.LocalConnection)) 13 | (alter-var-root #'adi-db component/stop)) 14 | 15 | (deftest start-adi-idempotent 16 | (alter-var-root #'adi-db component/start) 17 | (let [connection (:connection adi-db)] 18 | (alter-var-root #'adi-db component/start) 19 | (is (identical? connection (:connection adi-db)))) 20 | (alter-var-root #'adi-db component/stop)) 21 | 22 | (deftest stop-adi 23 | (alter-var-root #'adi-db component/start) 24 | (alter-var-root #'adi-db component/stop) 25 | (is (nil? (:connection adi-db)))) 26 | -------------------------------------------------------------------------------- /test/system/components/aleph_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.aleph-test 2 | (:require [system.components.aleph :refer [new-web-server]] 3 | [com.stuartsierra.component :as component] 4 | [clojure.test :refer [testing deftest is]])) 5 | 6 | (defn handler [request] 7 | {:status 200 8 | :headers {"Content-Type" "text/html"} 9 | :body "Hello World"}) 10 | 11 | (def http-server (new-web-server 8081 handler)) 12 | 13 | (deftest http-server-lifecycle 14 | (alter-var-root #'http-server component/start) 15 | (is (:server http-server) "HTTP server has been added to component") 16 | (alter-var-root #'http-server component/stop)) 17 | -------------------------------------------------------------------------------- /test/system/components/benjamin_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.benjamin-test 2 | (:require [benjamin.core :as b] 3 | [benjamin.configuration :refer [config]] 4 | [system.components.benjamin :refer [new-logbook]] 5 | [com.stuartsierra.component :as component] 6 | [clojure.test :refer [deftest testing is]])) 7 | 8 | 9 | (deftest configuration 10 | (testing "Sanity check" 11 | (is (nil? b/success-fn)) 12 | (is ((:success-fn config))) 13 | (component/start (new-logbook :success-fn (constantly false))) 14 | (is (not ((:success-fn config)))) 15 | (is (not (with-bindings {#'b/success-fn (:success-fn config)} 16 | (b/success-fn)))))) 17 | -------------------------------------------------------------------------------- /test/system/components/core_async_pubsub_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.core-async-pubsub-test 2 | (:require [system.components.core-async-pubsub :refer [new-pubsub]] 3 | [com.stuartsierra.component :as component] 4 | [clojure.core.async :as a :refer [!! >! !! c {:topic :president :man (rand-nth presidents)}) 25 | (>!! c {:topic :scientist :woman (rand-nth scientists)}) 26 | ( (res/response "Example.") 38 | (res/content-type "text/plain"))) 39 | (not-found "

Page not found

"))) 40 | 41 | (defn system [port router routes] 42 | (component/system-map 43 | :routes (new-endpoint routes) 44 | :middleware (new-middleware {:middleware [[wrap-defaults api-defaults]]}) 45 | :handler (-> (new-handler :router router) 46 | (component/using [:routes :middleware])) 47 | :http (-> (new-web-server port) 48 | (component/using [:handler])))) 49 | ;; ============================================================================= 50 | ;; End of bidi system setup 51 | 52 | (def bidi-system (system 8081 :bidi bidi-routes)) 53 | (def compojure-system (system 8082 :compojure compojure-routes)) 54 | 55 | (defn fixtures [f] 56 | (alter-var-root #'bidi-system component/start) 57 | (alter-var-root #'compojure-system component/start) 58 | (f) 59 | (alter-var-root #'compojure-system component/stop) 60 | (alter-var-root #'bidi-system component/stop)) 61 | 62 | (use-fixtures :each fixtures) 63 | 64 | (deftest check-instance 65 | (is (instance? com.stuartsierra.component.SystemMap compojure-system)) 66 | (is (instance? com.stuartsierra.component.SystemMap bidi-system)) 67 | (is (instance? system.components.endpoint.Endpoint (:routes bidi-system))) 68 | (is (instance? system.components.middleware.Middleware (:middleware bidi-system))) 69 | (is (instance? system.components.handler.Handler (:handler bidi-system))) 70 | (is (instance? system.components.endpoint.Endpoint (:routes compojure-system))) 71 | (is (instance? system.components.middleware.Middleware (:middleware compojure-system))) 72 | (is (instance? system.components.handler.Handler (:handler compojure-system)))) 73 | 74 | (deftest router 75 | (is (= #'bidi.ring/make-handler (get-in bidi-system [:handler :router]))) 76 | (is (= #'compojure/routes (get-in compojure-system [:handler :router])))) 77 | 78 | (deftest handler 79 | (is (= ((:handler (:handler compojure-system)) (mock/request :get "/")) 80 | {:status 200 81 | :headers {"Content-Type" "text/plain; charset=utf-8"} 82 | :body "Example."})) 83 | (is (= ((:handler (:handler bidi-system)) (mock/request :get "/index.html")) 84 | {:status 200 85 | :headers {"Content-Type" "text/html; charset=utf-8"} 86 | :body "Homepage"}))) 87 | 88 | (deftest default-system-is-compojure 89 | (let [default-system (component/system-map 90 | :routes (new-endpoint compojure-routes) 91 | :middleware (new-middleware {:middleware [[wrap-defaults api-defaults]]}) 92 | :handler (-> (new-handler) 93 | (component/using [:routes :middleware])) 94 | :http (-> (new-web-server 8083) 95 | (component/using [:handler]))) 96 | running-system (component/start default-system)] 97 | (is (instance? com.stuartsierra.component.SystemMap default-system)) 98 | (is (= #'compojure/routes (get-in default-system [:handler :router]))) 99 | (is (= ((:handler (:handler running-system)) (mock/request :get "/")) 100 | {:status 200 101 | :headers {"Content-Type" "text/plain; charset=utf-8"} 102 | :body "Example."})) 103 | (component/stop running-system))) 104 | -------------------------------------------------------------------------------- /test/system/components/hara_io_scheduler_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.hara-io-scheduler-test 2 | (:require 3 | [com.stuartsierra.component :as component] 4 | [hara.io.scheduler :as sch] 5 | [hara.io.scheduler.clock :refer [clock-started?]] 6 | [system.components.hara-io-scheduler :refer [new-scheduler]] 7 | [clojure.test :refer [deftest is]])) 8 | 9 | (def scheduler (new-scheduler (sch/scheduler {}))) 10 | 11 | (deftest scheduled-executor-service-test 12 | (alter-var-root #'scheduler component/start) 13 | (is (= (clock-started? (get-in scheduler [:scheduler :clock])) true)) 14 | (alter-var-root #'scheduler component/stop) 15 | (is (= system.components.hara_io_scheduler.Scheduler (type scheduler))) 16 | (is (nil? (:scheduler scheduler)))) 17 | -------------------------------------------------------------------------------- /test/system/components/hikari_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.hikari-test 2 | (:use clojure.test) 3 | (:require [system.components.hikari :as h] 4 | [clojure.java.jdbc :as jdbc] 5 | [com.stuartsierra.component :as component])) 6 | 7 | (deftest hikari-test 8 | (let [db (h/new-hikari-cp h/DEFAULT-H2-MEM-SPEC) 9 | db (component/start db)] 10 | (is (= (type (:datasource db)) 11 | com.zaxxer.hikari.HikariDataSource)) 12 | (jdbc/execute! db ["CREATE TABLE Temp (id int, name varchar);"]) 13 | (jdbc/insert! db "Temp" {:id 1 :name "Bob"}) 14 | (is (= "Bob" (:name (first (jdbc/query db ["SELECT * FROM Temp;"]))))) 15 | (is (nil? (:datasource (component/stop db)))))) 16 | -------------------------------------------------------------------------------- /test/system/components/http_kit_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.http-kit-test 2 | (:require [system.components.http-kit :refer [new-web-server new-http-kit]] 3 | [clj-http.client :as client] 4 | [com.stuartsierra.component :as component] 5 | [clojure.test :refer [testing deftest is]])) 6 | 7 | (defn handler [request] 8 | {:status 200 9 | :headers {"Content-Type" "text/html"} 10 | :body "Hello World"}) 11 | 12 | (def http-server (new-web-server 8081 handler)) 13 | 14 | (deftest http-server-lifecycle 15 | (alter-var-root #'http-server component/start) 16 | (is (:server http-server) "HTTP server has been added to component") 17 | (alter-var-root #'http-server component/stop) 18 | (is (= system.components.http_kit.WebServer (type http-server)))) 19 | 20 | (deftest http-server-options-invalid-key-throws 21 | (is (thrown? RuntimeException (new-web-server 8080 handler {:threads 7})))) 22 | 23 | (deftest http-server-options-invalid-value-throws 24 | (is (thrown? RuntimeException (new-web-server 8080 handler {:thread 7.7})))) 25 | 26 | (deftest http-server-options-invalid-port-throws 27 | (is (thrown? RuntimeException (new-web-server -1 handler {:thread 7})))) 28 | 29 | (deftest new-constructor 30 | (let [server (component/start (new-http-kit :port 8081 :handler handler))] 31 | (is (= "Hello World" (:body (client/get "http://localhost:8081")))) 32 | (component/stop server))) 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/system/components/immutant_web_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.immutant-web-test 2 | (:require [system.components.immutant-web :refer [new-web-server new-immutant-web]] 3 | [com.stuartsierra.component :as component] 4 | [clj-http.client :as client] 5 | [clojure.test :refer [testing deftest is]])) 6 | 7 | (defn handler [request] 8 | {:status 200 9 | :headers {"Content-Type" "text/html"} 10 | :body "Hello World"}) 11 | 12 | (defn handler2 [request] 13 | {:status 200 14 | :headers {"Content-Type" "text/html"} 15 | :body "Hello Universe"}) 16 | 17 | (def http-server (new-web-server 8081 handler)) 18 | 19 | (deftest http-server-lifecycle 20 | (alter-var-root #'http-server component/start) 21 | (is (:server http-server) "HTTP server has been added to component") 22 | (alter-var-root #'http-server component/stop) 23 | (is (= system.components.immutant_web.WebServer (type http-server)))) 24 | 25 | (deftest http-server-illegal-port-throws 26 | (is (thrown? RuntimeException (new-web-server -1 handler)))) 27 | 28 | (deftest http-server-illegal-dispatch-option-throws 29 | (is (thrown? RuntimeException (new-web-server 8080 handler {:dispatch? "bob"})))) 30 | 31 | (deftest one-handler 32 | (let [server (component/start (new-immutant-web :port 8083 :handler handler))] 33 | (is (= "Hello World" (:body (client/get "http://localhost:8083")))) 34 | (component/stop server))) 35 | 36 | (deftest two-handlers 37 | (let [server (component/start (new-immutant-web :port 8083 :handler handler)) 38 | server (component/start (new-immutant-web :port 8083 :handler handler2 :options {:path "second"}))] 39 | (is (= "Hello World" (:body (client/get "http://localhost:8083")))) 40 | (is (= "Hello Universe" (:body (client/get "http://localhost:8083/second")))) 41 | (component/stop server))) 42 | -------------------------------------------------------------------------------- /test/system/components/jdbc_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.jdbc-test 2 | (:use clojure.test) 3 | (:use system.components.h2)) 4 | 5 | -------------------------------------------------------------------------------- /test/system/components/jetty_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.jetty-test 2 | (:require 3 | [com.stuartsierra.component :as component] 4 | [system.components.jetty :refer [new-web-server]] 5 | (system.monitoring jetty 6 | [core :refer [started? stopped?]]) 7 | [clojure.test :refer [testing deftest is]])) 8 | 9 | (defn handler [request] 10 | {:status 200 11 | :headers {"Content-Type" "text/html"} 12 | :body "Hello World"}) 13 | 14 | (def http-server (new-web-server 8081 handler)) 15 | 16 | (deftest http-server-lifecycle 17 | (alter-var-root #'http-server component/start) 18 | (is (:server http-server) "HTTP server has been added to component") 19 | (is (.isStarted (:server http-server)) "HTTP server starts") 20 | (is (not (.isStopped (:server http-server))) "HTTP server stops") 21 | (alter-var-root #'http-server component/stop) 22 | (is (= system.components.jetty.WebServer (type http-server)))) 23 | 24 | (deftest http-server-valid-options-does-not-throw 25 | (is (new-web-server 8081 handler {:host "example.com" 26 | :configurator {} 27 | :join? false 28 | :daemon? true 29 | :ssl-port 8443 30 | :keystore "/tmp/keystore" 31 | :key-password "keeping" 32 | :truststore "/tmp/truststore" 33 | :trust-password "secrets" 34 | :max-threads 16 35 | :min-threads 2 36 | :max-idle-time 12345 37 | :client-auth {} 38 | :send-date-header? true 39 | :output-buffer-size 12345 40 | :request-header-size 12345 41 | :response-header-size 12345}))) 42 | 43 | 44 | (deftest http-server-port-too-low-throws 45 | (is (thrown? RuntimeException (new-web-server -1 handler)))) 46 | 47 | (deftest http-server-port-too-high-throws 48 | (is (thrown? RuntimeException (new-web-server 65536 handler)))) 49 | 50 | (deftest http-server-max-threads-too-low-throws 51 | (is (thrown? RuntimeException (new-web-server 8080 handler {:max-threads 0})))) 52 | 53 | (deftest http-server-monitoring-status 54 | (alter-var-root #'http-server component/start) 55 | (is (started? http-server)) 56 | (is (not (stopped? http-server))) 57 | (alter-var-root #'http-server component/stop) ) 58 | -------------------------------------------------------------------------------- /test/system/components/kampbell_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.kampbell-test 2 | (:require [system.components 3 | [kampbell :as kampbell] 4 | [konserve :as konserve]] 5 | [com.stuartsierra.component :as component] 6 | [clojure.core.async :refer [ (component/system-map 71 | :db (konserve/new-konserve :type :filestore :path *db-path* :serializer (m/fressian-serializer)) 72 | :kampbell (component/using (kampbell/new-kampbell :equality-specs #{:domain.utils/created-at} :entities #{"users"}) [:db])) 73 | component/start) 74 | db (:store (:db system))] 75 | (is (some? db)) 76 | (is (= #{["users"]} (k/list-collections db))) 77 | (is (contains? kampbell.core/equality-specs :created-at)) 78 | (is (contains? kampbell.core/equality-specs :domain.utils/created-at)) 79 | (is (empty? (get-users db))) 80 | (save-user db good-input) 81 | (is (= 1 (count (get-users db)))) 82 | (save-user db good-input) 83 | (is (= 1 (count (get-users db)))) 84 | (is (thrown-with-msg? clojure.lang.ExceptionInfo #"Invalid input" (save-user db bad-input))) 85 | (save-user db (assoc good-input :domain.user/name "Alan Kay")) 86 | (is (= 2 (count (get-users db)))) 87 | (let [user (get-user db "daniel@szmulewicz.com")] 88 | (is (= "Daniel Szmulewicz" (:domain.user/name user))) 89 | (update-user db (assoc user :domain.user/name "Hans Solo") :domain.user/name)) 90 | (let [user (get-user db "daniel@szmulewicz.com")] 91 | (s/valid? :domain/user user)) 92 | (let [user (get-user db "daniel@szmulewicz.com")] 93 | (is (thrown-with-msg? clojure.lang.ExceptionInfo #"Invalid input" (update-user db (assoc user :domain.user/name 1234) :domain.user/name)))) 94 | (let [user (get-user db "daniel@szmulewicz.com")] 95 | (is (= "Hans Solo" (:domain.user/name user))) 96 | (update-user db (assoc user :domain.user/name "Greedo" :domain.user/email "greedo@greedo.com") :domain.user/name :domain.user/email)) 97 | (is (some #(= "Greedo" (:domain.user/name %)) (get-users db))) 98 | (let [user (get-user db "greedo@greedo.com")] 99 | (update-user db (assoc user :domain.user/address "Stairway to Heaven 669") :domain.user/address) 100 | (is (= "Stairway to Heaven 669" (:domain.user/address (get-user db "greedo@greedo.com"))))) 101 | (let [user (get-user db "greedo@greedo.com")] 102 | (is (true? (contains? user :domain.user/address)))) 103 | (let [user (get-user db "greedo@greedo.com")] 104 | (update-user db (dissoc user :domain.user/address) :domain.user/address) 105 | (is (nil? (:domain.user/address (get-user db "greedo@greedo.com"))))) 106 | (let [user (get-user db "greedo@greedo.com")] 107 | (is (false? (contains? user :domain.user/address)))) 108 | (let [user (get-user db "greedo@greedo.com")] 109 | (update-user db (dissoc user :domain.user/name) :domain.user/name) 110 | (is (nil? (:domain.user/name (get-user db "greedo@greedo.com"))))) 111 | (let [user (get-user db "greedo@greedo.com")] 112 | (is (false? (s/valid? :domain/user user)))) ;; doesn't catch absence of required keys, breaks integrity guarantee 113 | (let [user (get-user db "greedo@greedo.com")] 114 | (delete-user db user)) 115 | (is (= 1 (count (get-users db)))) 116 | (component/stop system))) 117 | -------------------------------------------------------------------------------- /test/system/components/konserve_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.konserve-test 2 | (:require [system.components.konserve :as c] 3 | [konserve.core :as k] 4 | [konserve.serializers :as s] 5 | [konserve.filestore :refer [delete-store list-keys]] 6 | [com.stuartsierra.component :as component] 7 | [clojure.core.async :as async :refer [ (count (monger.collection/indexes-on (:db mongo-db-with-options-and-init) "users")) 1)) 66 | (is (> (count (monger.collection/indexes-on (:db mongo-db-with-options-and-init) "coll1")) 1)) 67 | (println (get (cmd/server-status (:db mongo-db-with-options-and-init)) "connections")) 68 | (alter-var-root db component/stop) 69 | (is (nil? (:db mongo-db-with-options-and-init)) "DB is stopped"))) 70 | 71 | (deftest ^:dependency mongo-development 72 | (alter-var-root #'mongo-db-dev component/start) 73 | (is (:db mongo-db-dev) "DB has been added to component") 74 | (create-and-drop-collection (:db mongo-db-dev)) 75 | (alter-var-root #'mongo-db-dev component/stop) 76 | (is (nil? (:db mongo-db-dev)) "DB is stopped")) 77 | 78 | (deftest ^:dependency mongo-development-idempotence 79 | (alter-var-root #'mongo-db-dev component/start) 80 | (test-open-connections #(alter-var-root #'mongo-db-dev component/start)) 81 | (alter-var-root #'mongo-db-dev component/stop) 82 | (is (nil? (:db mongo-db-dev)) "DB is stopped")) 83 | 84 | (deftest ^:dependency mongo-indices 85 | (alter-var-root #'mongo-with-indices component/start) 86 | (is (:db mongo-with-indices) "DB has been added to component") 87 | (is (= clojure.lang.PersistentHashSet (type (db/get-collection-names (:db mongo-with-indices)))) "Collections on DB is a set") 88 | (is (monger.collection/exists? (:db mongo-with-indices) "users")) 89 | (is (monger.collection/exists? (:db mongo-with-indices) "coll1")) 90 | (is (> (count (monger.collection/indexes-on (:db mongo-with-indices) "users")) 1)) 91 | (is (> (count (monger.collection/indexes-on (:db mongo-with-indices) "coll1")) 1)) 92 | (alter-var-root #'mongo-with-indices component/stop) 93 | (is (nil? (:db mongo-with-indices)) "DB is stopped")) 94 | 95 | (deftest ^:dependency mongo-with-bogus-options 96 | (is (= {:bogus 'disallowed-key} (try (new-mongo-db "127.0.0.1" 27017 "test" {:bogus true}) 97 | (catch clojure.lang.ExceptionInfo e (:error (ex-data e))))))) 98 | -------------------------------------------------------------------------------- /test/system/components/neo4j_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.neo4j-test 2 | (:use clojure.test) 3 | (:require [system.components.neo4j :as neo4j] 4 | [clojurewerkz.neocons.rest.nodes :as nodes :only [create get delete]] 5 | [com.stuartsierra.component :as component])) 6 | 7 | (def uri "http://localhost:7474/db/data/") 8 | (def data {:foo "bar"}) 9 | 10 | (deftest ^:dependency test-neo4j 11 | (let [{:keys [conn] :as db} (-> (neo4j/new-neo4j-db uri) 12 | component/start)] 13 | (is conn "conn has been added to component") 14 | 15 | (let [node (nodes/create conn data)] 16 | (is data (:data (nodes/get conn (:id node)))) 17 | (nodes/delete conn (:id node))) 18 | 19 | (let [db (component/stop db)] 20 | (is (nil? (:conn db)) "conn has been removed from component")))) 21 | -------------------------------------------------------------------------------- /test/system/components/postgres_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.postgres-test 2 | (:use clojure.test) 3 | (:require [system.components.postgres :as p] 4 | [clojure.java.jdbc :as jdbc] 5 | [com.stuartsierra.component :as component])) 6 | 7 | 8 | ;; Assumes you have run `script/pg_test_setup.sh' 9 | 10 | (def test-db-spec 11 | {:classname "org.postgresql.Driver" 12 | :subprotocol "postgresql" 13 | :subname "system_test_db" 14 | :user "system_test_user" 15 | :password "" 16 | :host "127.0.0.1"}) 17 | 18 | (deftest ^:dependency postgres-test-create-table-and-insert 19 | (let [db (component/start 20 | (p/new-postgres-database test-db-spec)) 21 | msg "It works!"] 22 | (jdbc/execute! db ["CREATE TEMP TABLE test (coltest varchar(20));"]) 23 | (jdbc/insert! db :test {:coltest msg}) 24 | (is (= msg (:coltest (first (jdbc/query db ["SELECT * from test;"]))))) 25 | (component/stop db))) 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/system/components/quartzite_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.quartzite-test 2 | (:require 3 | [com.stuartsierra.component :as component] 4 | [system.components.quartzite :as quartz] 5 | (system.monitoring quartzite 6 | [core :refer [started? stopped?]]) 7 | [clojurewerkz.quartzite.scheduler :as qs] 8 | [clojure.test :refer [deftest is]])) 9 | 10 | (def scheduler (quartz/new-scheduler)) 11 | 12 | (deftest quartzite-test 13 | (alter-var-root #'scheduler component/start) 14 | (is (qs/started? (:scheduler scheduler)) "the scheduler is running") 15 | (alter-var-root #'scheduler component/stop) 16 | (is (qs/shutdown? (:scheduler scheduler)) "the scheduler is stopped")) 17 | 18 | 19 | (deftest quartzite-monitoring-status 20 | (alter-var-root #'scheduler component/start) 21 | (is (started? scheduler)) 22 | (alter-var-root #'scheduler component/stop) 23 | (is (stopped? scheduler))) 24 | -------------------------------------------------------------------------------- /test/system/components/redis_pubsub_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.redis-pubsub-test 2 | (:require [system.components.redis-pubsub :as sut] 3 | [taoensso.carmine :as car :refer (wcar)] 4 | [clojure.test :refer [deftest is]] 5 | [com.stuartsierra.component :as component])) 6 | 7 | (deftest ^:dependency redis-pubsub 8 | (let [topic "*" 9 | msg (atom []) 10 | handler (fn [[_ _ _ message]] 11 | (when message (swap! msg conj message))) 12 | pubsub (component/start 13 | (sut/new-redis-pubsub topic handler))] 14 | (car/wcar (:conn pubsub) (car/publish "foobar" "Hello to foobar!")) 15 | (is (= @msg ["Hello to foobar!"])) 16 | (component/stop pubsub))) 17 | -------------------------------------------------------------------------------- /test/system/components/redis_queue_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.redis-queue-test 2 | (:require [system.components.redis-queue :as sut] 3 | [com.stuartsierra.component :as component] 4 | [taoensso.carmine :as car] 5 | [taoensso.carmine.message-queue :as car-mq] 6 | [clojure.test :refer [deftest is testing]])) 7 | 8 | (def counter (atom 0)) 9 | 10 | (deftest ^:dependency redis-queue 11 | (testing "Testing the core functionality" 12 | (let [queue (component/start (sut/new-redis-queue :xs [{:f (fn [component] 13 | (fn [{:keys [message attempt]}] 14 | (is (= (:iter message) @counter)) 15 | (swap! counter inc) 16 | (println "Received" (:event message)) 17 | {:status :success})) 18 | :q :testing}]))] 19 | (dotimes [n 10] (car/wcar (:conn queue) (car-mq/enqueue :testing {:event (name (gensym)) :iter n}))) 20 | (Thread/sleep 30000) 21 | (is (= 10 @counter)) 22 | (component/stop queue)))) 23 | -------------------------------------------------------------------------------- /test/system/components/redis_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.redis-test 2 | (:require [system.components.redis :as r] 3 | [com.stuartsierra.component :as component] 4 | [taoensso.carmine :as car :refer [wcar]] 5 | [clojure.test :refer [deftest is testing]])) 6 | 7 | (deftest ^:dependency redis 8 | (testing "Test the core API" 9 | (let [db (component/start (r/new-redis)) 10 | conn (:server-conn db) 11 | k (name (gensym))] 12 | (is (= 1 (wcar conn (car/sadd k "bar")))) 13 | (is (= 0 (wcar conn (car/sadd k "bar")))) 14 | (wcar conn (car/srem k "bar")) 15 | (component/stop db)))) 16 | -------------------------------------------------------------------------------- /test/system/components/repl_server_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.repl-server-test 2 | (:require 3 | [system.components.repl-server :refer [new-repl-server]] 4 | [com.stuartsierra.component :as component] 5 | [clojure.test :refer [deftest is run-tests]] 6 | [clojure.tools.nrepl :as repl])) 7 | 8 | (defn connect [code] 9 | (with-open [conn (repl/connect :port 8082)] 10 | (-> (repl/client conn 1000) 11 | (repl/message {:op :eval :code code}) 12 | repl/response-values))) 13 | 14 | (deftest repl-server-availability 15 | (let [server (component/start (new-repl-server :port 8082))] 16 | (is (:server server) "REPL server has been added to component") 17 | (is (= [2] (connect "(+ 1 1)")) "REPL functions normally") 18 | (component/stop server))) 19 | 20 | (deftest repl-server-with-cider 21 | (let [server (component/start (new-repl-server :port 8082 :with-cider true))] 22 | (is (:server server) "REPL server has been added to component") 23 | (component/stop server))) 24 | 25 | (deftest repl-server-bind 26 | (let [server (component/start (new-repl-server :port 8082 :bind "0.0.0.0"))] 27 | (is (:server server) "REPL server has been added to component") 28 | (is (= [2] (connect "(+ 1 1)")) "REPL functions normally") 29 | (component/stop server))) 30 | 31 | (deftest repl-server-wrong-bind 32 | (is (thrown-with-msg? java.net.BindException #"Cannot assign requested address" 33 | (component/start (new-repl-server :port 8082 :bind "1.0.0.0"))))) 34 | -------------------------------------------------------------------------------- /test/system/components/riemann_client_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.riemann-client-test 2 | (:require 3 | [com.stuartsierra.component :as component] 4 | [system.components.riemann-client :refer [new-riemann-client]] 5 | (system.monitoring riemann-client 6 | [core :refer [started? stopped?]]) 7 | [clojure.test :refer [testing deftest is]])) 8 | 9 | 10 | (deftest ^:dependency riemann-client 11 | (let [client (component/start (new-riemann-client))] 12 | (is (started? client)) 13 | (component/stop client) 14 | (is (stopped? client)))) 15 | 16 | (deftest ^:dependency riemann-send-fn 17 | (let [client (component/start (new-riemann-client)) 18 | send-fn (:send-fn client)] 19 | (Thread/sleep 1000) 20 | (is (started? client)) 21 | (is (= clojure.lang.Agent (type (send-fn {:service "foo" :state "ok"})))) 22 | (Thread/sleep 1000) 23 | (is (= io.riemann.riemann.client.MapPromise (type (:promise @(send-fn {:service "foo" :state "ok"}))))) 24 | (is (= riemann.codec.Msg (type (deref (:promise @(send-fn {:service "foo" :state "ok"})))))) 25 | (Thread/sleep 1000) 26 | (component/stop client))) 27 | -------------------------------------------------------------------------------- /test/system/components/scheduled_executor_service_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.scheduled-executor-service-test 2 | (:require 3 | [com.stuartsierra.component :as component] 4 | [system.components.scheduled-executor-service :refer [new-scheduler]] 5 | (system.monitoring scheduled-executor-service 6 | [core :refer [started? stopped?]]) 7 | [clojure.test :refer [deftest is]]) 8 | (:import [java.util.concurrent TimeUnit ScheduledThreadPoolExecutor])) 9 | 10 | (def workers [{:f (fn [component] #(println (str "fixed-rate " (:n-threads component)))) :initial-delay 0 :period 1 :unit TimeUnit/MINUTES :method :fixed-rate} 11 | {:f (fn [component] #(println (str "fixed-delay " (:n-threads component)))) :initial-delay 0 :period 1 :unit TimeUnit/MINUTES :method :fixed-delay} 12 | {:f (fn [component] #(println (str "one-off " (:n-threads component)))) :initial-delay 0 :unit TimeUnit/MINUTES :method :one-off}]) 13 | 14 | (def scheduler (new-scheduler :xs workers)) 15 | (def scheduler-with-n-threads (new-scheduler :xs workers :n-threads (+ 2 (.availableProcessors (Runtime/getRuntime))))) 16 | 17 | (deftest scheduled-executor-service-test 18 | (alter-var-root #'scheduler component/start) 19 | (is (= ScheduledThreadPoolExecutor (type (:scheduler scheduler))) "the scheduler is running") 20 | (alter-var-root #'scheduler component/stop) 21 | (is (.isShutdown (:scheduler scheduler)) "the scheduler is stopped")) 22 | 23 | (deftest scheduled-executor-service-with-threads-test 24 | (alter-var-root #'scheduler-with-n-threads component/start) 25 | (is (= ScheduledThreadPoolExecutor (type (:scheduler scheduler-with-n-threads))) "the scheduler is running") 26 | (alter-var-root #'scheduler-with-n-threads component/stop) 27 | (is (.isShutdown (:scheduler scheduler-with-n-threads)) "the scheduler is stopped")) 28 | 29 | (deftest scheduler-monitoring-status 30 | (alter-var-root #'scheduler component/start) 31 | (is (started? scheduler)) 32 | (alter-var-root #'scheduler component/stop) 33 | (is (stopped? scheduler))) 34 | -------------------------------------------------------------------------------- /test/system/components/sente_client_test.cljs: -------------------------------------------------------------------------------- 1 | (ns system.components.sente-client-test 2 | (:require [system.components.sente :refer [new-channel-socket-client]] 3 | [com.stuartsierra.component :as component] 4 | [taoensso.sente :as sente] 5 | [cljs.test :refer-macros [deftest testing is]])) 6 | 7 | (def channel-sockets (atom (new-channel-socket-client (fn []) "/chsk"))) 8 | 9 | (deftest channel-sockets-lifecycle 10 | (swap! channel-sockets component/start) 11 | (is (ifn? (:chsk-send! @channel-sockets))) 12 | (is (not (nil? (:chsk @channel-sockets)))) 13 | (is (not (nil? (:ch-chsk @channel-sockets)))) 14 | (is (not (nil? (:chsk-state @channel-sockets)))) 15 | (swap! channel-sockets component/stop)) 16 | 17 | -------------------------------------------------------------------------------- /test/system/components/sente_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.sente-test 2 | (:require [system.components.sente :refer [new-channel-socket-server]] 3 | [com.stuartsierra.component :as component] 4 | [taoensso.sente :as sente] 5 | [taoensso.sente.server-adapters.http-kit :refer [http-kit-adapter]] 6 | [clojure.test :refer [deftest testing is]])) 7 | 8 | (def channel-sockets (new-channel-socket-server (fn []) http-kit-adapter)) 9 | 10 | (deftest channel-sockets-lifecycle 11 | (alter-var-root #'channel-sockets component/start) 12 | (is (ifn? (:chsk-send! channel-sockets))) 13 | (is (map? @(:connected-uids channel-sockets))) 14 | (alter-var-root #'channel-sockets component/stop)) 15 | -------------------------------------------------------------------------------- /test/system/components/watcher_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.components.watcher-test 2 | (:require [system.components.watcher :refer :all] 3 | (system.monitoring watcher 4 | [core :refer [started? stopped?]]) 5 | [com.stuartsierra.component :as component] 6 | [clojure.test :refer [testing deftest is]])) 7 | 8 | (deftest watcher-lifecycle 9 | (is [:create "Hello"] 10 | 11 | (let [box (atom nil) 12 | result (promise) 13 | wtch (new-watcher ["."] 14 | (fn [kind file] 15 | (component/stop @box) 16 | (deliver result [kind (slurp file)]) 17 | (.delete file)) 18 | {:filter [".watcher"] 19 | :recursive false}) 20 | _ (reset! box (component/start wtch))] 21 | (spit "test.watcher" "Hello") 22 | @result))) 23 | 24 | (deftest start-watcher-idempotent 25 | (let [watcher (new-watcher ["."] 26 | (constantly nil) 27 | {}) 28 | started (component/start watcher)] 29 | (is (identical? started (component/start started))) 30 | (component/stop watcher))) 31 | 32 | (deftest stop-watcher-idempotent 33 | (let [watcher (new-watcher ["."] 34 | (constantly nil) 35 | {}) 36 | started (component/start watcher) 37 | stopped (component/stop started)] 38 | (is (identical? stopped (component/stop stopped))))) 39 | 40 | (deftest watcher-monitoring 41 | (let [watcher (new-watcher ["."] 42 | (constantly nil) 43 | {})] 44 | (is (started? (component/start watcher))) 45 | (is (stopped? (component/stop watcher))))) 46 | 47 | -------------------------------------------------------------------------------- /test/system/schema_test.clj: -------------------------------------------------------------------------------- 1 | (ns system.schema-test 2 | (:require [system.schema :as sc] 3 | [schema.core :as s] 4 | [clojure.test :refer [deftest is]])) 5 | 6 | (deftest port-lt-0-throws 7 | (is (thrown? RuntimeException (s/validate sc/Port -1)))) 8 | 9 | (deftest port-gt-65535-throws 10 | (is (thrown? RuntimeException (s/validate sc/Port 65536)))) 11 | 12 | (deftest port-0-valid 13 | (is (= 0 (s/validate sc/Port 0)))) 14 | 15 | (deftest port-65535-valid 16 | (is (= 65535 (s/validate sc/Port 65535)))) 17 | 18 | (deftest port-float-throws 19 | (is (thrown? RuntimeException (s/validate sc/Port 0.0)))) 20 | 21 | (deftest posint-0-throws 22 | (is (thrown? RuntimeException (s/validate sc/PosInt 0)))) 23 | 24 | (deftest posint-1-valid 25 | (is (= 1 (s/validate sc/PosInt 1)))) 26 | 27 | (deftest posint-float-throws 28 | (is (thrown? RuntimeException (s/validate sc/PosInt 1.0)))) 29 | --------------------------------------------------------------------------------