0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-30 10:51:09 -05:00
penpot/backend/vendor/vertx/README.md
2020-02-17 09:41:00 +01:00

250 lines
7 KiB
Markdown

# vertx-clojure
A lightweight clojure adapter for vertx toolkit.
- **STATUS**: *alpha*, in design and prototyping phase.
- **AUDIENCE**: this is not a vertx documentation, this readme intends
to explain only the clojure api
Example code on `resources/user.clj` file.
## Install
Using `deps.edn`:
```clojure
vertx-clojure/vertx {:mvn/version "0.0.0-SNAPSHOT"}
```
Using Leiningen:
```clojure
[vertx-clojure/vertx "0.0.0-SNAPSHOT"]
```
## User Guide
### Verticles
Verticles is the basic "unit of execution" in the vertx toolkit. The
concept is very simular to actors with the exception that a verticle
does not have a inbox, and verticles communicates with other verticles
or with the rest of the world using **eventbus**.
For create a verticle, you will need to create first a system instance
(a name that we give to the `Vertx` instance):
```clojure
(require '[vertx.core :as vc])
(def system (vc/system))
```
Then, you can proceed to create a verticle. A verticle concist on
three functions: `on-start`, `on-stop` and `on-error` (where the
`on-start` is mandatory the rest optional).
Lets define a dummy verticle that just prints hello world on start
callback:
```clojure
(defn on-start
[ctx]
(println "Hello world"))
(def dummy-verticle
(vc/verticle {:on-start on-start}))
```
The `dummy-verticle` is a verticle factory, nothing is running at this
momment. For run the verticle we need to deploy it using the
previously created `system` instance:
```clojure
(vc/deploy! system dummy-verticle)
```
The `deploy!` return value is a `CompletionStage` so you can deref it
like a regular `Future` or use **funcool/promesa** for chain more
complex transformations. The return value implements `AutoCloseable`
that will allow to undeploy the verticle.
The `deploy!` function also accepts an additional parameter for
options, and at this momment it only accepts as single option:
- `:instances` - number of instances to launch of the same verticle.
- `:worker` - use worker thread pool or the default event-loop.
### Event Bus
The **eventbus** is the central communication system for verticles. It
has different patterns of communication. On this documentation we will
cover the: `publish/subscribe` and `request/reply`.
Lets define a simple echo verticle:
```clojure
(require '[vertx.eventbus :as ve])
(defn on-message
[msg]
(:body msg))
(defn on-start
[ctx]
(vc/consumer ctx "test.echo" on-message))
(def echo-verticle
(vc/verticle {:on-start on-start}))
```
And then, lets deploy 4 instances of it:
```clojure
(vc/deploy! system echo-verticle {:instances 4})
```
Now, depending on how you send the messages to the "test.echo" topic,
the message will be send to a single instance of will be broadcasted
to all verticle instances subscribed to it.
To send a message and expect a response we need to use the
`ve/request!` function:
```clojure
@(ve/request! system {:foo "bar"})
;; => #vertx.eventbus.Msg{:body {:foo "bar"}}
```
The return value of `on-message` callback will be used as a reply and
it can be any plain value or a `CompletionStage`.
When you want to send a message but you don't need the return value,
there is the `ve/send!` function. And finally, if you want to send a
message to all instances subscribed to a topic, you will need to use
the `ve/publish!` function.
### Http Server (vertx.http)
**STATUS**: pre-alpha: experimental & incomplete
This part will explain the low-level api for the http server. It is
intended to be used as a building block for a more higher-level api or
when you know that you exactly need for a performance sensitive
applications.
The `vertx.http` exposes two main functions `handler` and
`server`. Lets start creating a simple "hello world" http server:
```
(require '[vertx.http :as vh])
(defn hello-world-handler
[req]
{:status 200
:body "Hello world\n"})
(defn on-start
[ctx]
(vh/server {:handler (vh/handler hello-world-handler)
:port 2021}))
(->> (vc/verticle {:on-start on-start})
(vc/deploy! system))
```
NOTE: you can start the server without creating a verticle but you
will loss the advantage of scaling (using verticle instances
parameter).
The `req` object is a plain map with the following keys:
- `:method` the HTTP method.
- `:path` the PATH of the requested URI.
- `:headers` a map with string lower-cased keys of headers.
- `:vertx.http/request` the underlying vertx request instance.
- `:vertx.http/response` the underlying vertx response instance.
And the response object to the ring response, it can contain
`:status`, `:body` and `:headers`.
**WARNING:** at this moment there are no way to obtain directly the
body of request using clojure api, this is in **design** phase and we
need to think how to expose it correctly without constraint too much
the user code (if you have suggestions, please open an issue).
**NOTE**: If you want completly bypass the clojure api, pass a vertx
`Handler` instance to server instead of using
`vertx.http/handler`. There is the `vertx.util/fn->handler` helper
that converts a plain clojure function into raw `Handler` instance.
### Web Server (vertx.web)
**STATUS**: alpha
This part will explain the higher-level http/web server api. It is a
general purpose with more clojure friendly api. It uses
`reitit-core`for the routing and `sieppari` for interceptors.
Lets start with a complete example:
```clojure
(require '[vertx.http :as vh])
(require '[vertx.web :as vw])
(require '[vertx.web.middleware :as vwm])
(defn hello-world-handler
[req]
{:status 200
:body "Hello world!\n"})
(defn on-start
[ctx]
(let [routes [["/" {:middleware [vwm/cookies]
:handler hello-world-handler
:method :get}]]
handler (vw/handler ctx
(vw/assets "/static/*" {:root "resources/public/static"})
(vw/router routes))]
(vh/server {:handler handler
:port 2022})))
(->> (vc/verticle {:on-start on-start})
(vc/deploy! system))
```
The routes are defined using `reitit-core`. The request object is very
similar to the one explained in `vertx.http`.
The main difference with `vertx.http` is that the handler is called
when the body is ready to be used and is available under `:body`
keyword on the request.
All additional features such that reading the query/form params,
parse/write cookies, cors and file uploads are provided with additional middleware
wrappers:
- `vertx.web.middleware/uploads` parses the vertx uploaded file data
structure and expose it as clojure maps under `:uploads` key.
- `vertx.web.middleware/params` parses the query string and form
params in the body if the content-type is appropriate and exposes
them under `:params`.
- `vertx.web.middleware/cors` properly sets the CORS headers.
- `vertx.web.middleware/cookies` handles the cookies reading from
the request and cookies writing from the response.
## License ##
```
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
```