├── .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:
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 "