vertx-clj

0.1.1-SNAPSHOT


syntax sugars for vertx clojure development

Fork me on GitHub

dependencies

org.clojure/clojure
1.4.0
org.vert-x/vertx-core
1.3.0.final
org.vert-x/vertx-platform
1.3.0.final
org.clojure/tools.logging
0.2.3



(this space intentionally left almost blank)
 
(ns vertx-clj.core
  (:import (org.vertx.java.core Vertx Handler AsyncResultHandler)
           (org.vertx.java.core.http HttpServerRequest RouteMatcher)
           (org.vertx.java.core.streams Pump)
           (clojure.lang ArityException))
  (:require [clojure.string :as s])
  (:use vertx-clj.utils))

Converts a string to UpperCamelCase.

(defn- camelize
  [x]
  (s/join (map s/capitalize (s/split x #"-"))))

Deploy a module programmatically. Please see the modules manual for more information about modules.

(defn deploy-module
  ([module-name conf]
     (fn [vertx# container#]
       (.deployModule container# module-name (json conf))))
  ([module-name conf instances]
     (.deployModule container# module-name (json conf) instances))
  ([module-name conf instances done-handler]
     (fn [vertx# container#]
       (.deployModule container# module-name (json conf) instances done-handler)))  )
(defmacro run-verticles [& verts]
  `(fn [_# container#]
     (doseq [v# (list ~@verts)]
       (doseq [name# (vals (ns-interns v#))]
         (when-let [verticle# (:verticle (meta name#))]
           (.deployVerticle container# verticle#))))))

Define a vertx verticle:

 (defverticle http-server
   (http-listen 8080 "localhost"
                (req-handler http-server [req]
                             (end-req req "hello vertx"))))

As in a java verticle, vertx and container is available for the body.

The name is used to generate the Verticle, e.g. the previous verticle will generate a HttpServer.class file in your project :compile-path, which can be run as vertx run HttpServer. This is transparent to the user if the lein-vertx plugin is used to run the verticles.

(defmacro defverticle
  [vert body]
  (let [this (gensym "this")
        prefix (gensym "prefix-")
        vert-class (-> vert str camelize)]
    `(do
       (defn ~(vary-meta (symbol (str prefix "start")) assoc :verticle vert-class) [~this]
         (let [vertx# (.getVertx ~this)
               container# (.getContainer ~this)]
           (~body vertx# container#)))
       (gen-class
        :name ~vert-class
        :extends org.vertx.java.deploy.Verticle
        :prefix ~(str prefix)))))

Syntax sugar to create a org.vertx.java.core.Handler instance:

 (handler [data]
   (println data))
(defmacro handler
  [expr & body]
  `(proxy [Handler] []
     (handle ~expr
       ~@body)))

Take a list of verticles and deploy them programmatically.

(defmacro deploy-verticles
  [& args]
  `(defverticle ~(gensym "container")
     (run-verticles ~@args)))

Create a handler and attach it the .dataHandler callback of the first argument, usually a socket

(defmacro data-handler
  [sock expr & body]
  `(.dataHandler ~sock
                 (handler ~expr ~@body)))

The same as data-handler, except the passed in data is devided by a delimited record parser.

(defmacro frame-handler
  [sock expr & body]
  `(.dataHandler ~sock
                 (RecordParser/newDelimited "\n"
                                            (handler ~expr ~@body))))

Create a Pump on the passed two sockets, takes an optional parameter for whether start the pump immediately, Default false.

(defn pump
  ([sock1 sock2]
     (Pump/createPump sock1 sock2))
  ([sock1 sock2 start]
     (if start
       (.start (pump sock1 sock2))
       (pump sock1 sock2))))
 
(ns vertx-clj.file
  (:use vertx-clj.core)
  (:import [org.vertx.java.core AsyncResultHandler]))
(defn open-file
  [vertx filename handler]
  (-> vertx .fileSystem
      (.open filename handler)))
(defn close-file [f callback]
  (.close f callback))
(defmacro async-result-handler [expr & body]
  "Create a ```AsyncResultHandler```"
  `(proxy [AsyncResultHandler] []
    (handle ~expr
      ~@body)))
 
(ns vertx-clj.http
  (:use vertx-clj.core)
  (:import [org.vertx.java.core.http RouteMatcher HttpServerRequest]
           [org.vertx.java.core Handler]))

Create a HttpClient instance.

(defmacro http-connect
  [port host & body]
  `(fn [vertx# _#]
     (let [http-client# (.createHttpClient vertx#)]
       (doto http-client# (.setPort ~port) (.setHost ~host))
       ((fn [~'vertx ~'client] ~@body) vertx# http-client#))))

Create a HttpServer instance that listens on the specified port and host. instance of Vertx and HttpServer is available in the body as vertx and http-server.

(defmacro http-listen
  [port host & body]
  `(fn [vertx# _#]
     (let [http-server# (.createHttpServer vertx#)]
       ((fn [~'vertx ~'http-server] ~@body) vertx# http-server#)
       (.listen http-server# ~port ~host))))

Sinatra like route matching by RouterMatcher, a RouterMatcher instance is available for the body.

(defmacro http-route
  [port host & body]
  `(fn [vertx# container#]
     (let [http# (.createHttpServer vertx#)
           router# (RouteMatcher.)]
       ((fn [~'router] ~@body) router#)
       (-> http#
           (.requestHandler router#)
           (.listen ~port ~host)))))

Create a handler for websocket request.

(defmacro ws-handler
  [http-server expr & body]
  `(.websocketHandler ~http-server
                      (handler ~expr ~@body)))

Create a handler for http request.

(defmacro req-handler
  [http-server expr & body]
  `(.requestHandler ~http-server
                    (handler ~expr ~@body)))

Retrieve the value associated with the key from request parameters.

(defn params
  [^HttpServerRequest req key]
  (-> req .params (.get key)))

Get the headers of the request.

(defn headers
  [^HttpServerRequest req]
  (.headers req))

Send a get request and block before the body returned.

(defmacro get-now
  [client path & body]
  `(.getNow ~client ~path
            (proxy [Handler] []
              (handle [resp#]
                (.bodyHandler resp#
                              (proxy [Handler] []
                                (handle [data#]
                                  ((fn [~'buf] ~@body) data#))))))))

Syntax sugar for ending request.

(defn end-req
  ([req buf]
     (.end (.response req) buf))
  ([req]
     (.end (.response req))))

Handler for request end event.

(defn end-handler
  [req callback]
  (.endHandler req callback))

Http SendFile.

(defn send-file
  [req & paths]
  (.sendFile (.response req) (apply str paths)))
 
(ns vertx-clj.net
  (:use vertx-clj.core))

Create a NetServer and listen on specified port and host, vertx and sock-server is available in the body.

(defmacro sock-listen
  [port host & body]
  `(fn [vertx# _#]
     (let [sock-server# (.createNetServer vertx#)]
       ((fn [~'vertx ~'sock-server] ~@body) vertx# sock-server#)
       (.listen sock-server# ~port ~host))))

Create a NetClient, and connect specified port and host.

(defmacro sock-connect
  [port host body]
  `(fn [vertx# container#]
     (let [client# (.createNetClient vertx#)]
       (.connect client# ~port ~host ~body))))

Used by a NetServer, handle new connection.

(defmacro connect-handler
  [sock-server expr & body]
  `(.connectHandler ~sock-server
                    (handler ~expr ~@body)))

Used by a NetServer, handle connection close.

(defmacro closed-handler
  [sock expr & body]
  `(.closedHandler ~sock
                  (handler ~expr ~@body)))
 
(ns vertx-clj.route-matcher
  (:refer-clojure :exclude [get])
  (:import [org.vertx.java.core Handler]
           [org.vertx.java.core.http RouteMatcher]))

Helper methods for RouteMatcher, check vertx docuement for more info on route pattern matching.

(defn get
  [^RouteMatcher router path req-fn]
  (.get router path
    (proxy [Handler] []
      (handle [req]
        (req-fn req)))))
(defn get-with-regexp
  [^RouteMatcher router path req-fn]
  (.getWithRegEx router path
    (proxy [Handler] []
      (handle [req]
        (req-fn req)))))
 
(ns vertx-clj.sockjs
  (:import [org.vertx.java.core.json JsonObject JsonArray])
  (:use vertx-clj.utils))

Helper methods for SockJSServer.

(declare bound-obj prefix)

Setting up handlers for requests from sockjs client.

(defn install-app
  [sockjs-server pfx handler]
  (.installApp sockjs-server (prefix pfx) handler))

Setting up SockJS EventBus bridge.

(defn bridge
  [sockjs-server pfx inbound outbound]
  (let [in-permitted (bound-obj inbound)
        out-permitted (bound-obj outbound)
        eb-prefix (prefix pfx)]
    (.bridge sockjs-server eb-prefix in-permitted out-permitted)))
(defn- ^JsonArray bound-obj [clj-map]
  (-> (JsonArray.) (.add (json clj-map))))
(defn- ^JsonObject prefix [s]
  (-> (JsonObject.) (.putString "prefix" s)))
 
(ns vertx-clj.utils
  (:import [org.vertx.java.core.json JsonObject]))

Convert a map to JsonObject.

(defn ^JsonObject json
  [clj-map]
  (let [ret (JsonObject.)]
    (doseq [[k v] clj-map]
      (.putString ret (name k) (str v)))
    ret))