Primeiramente, fora Temer.
Segundamente, Compojure nada mais é que um "facilitador" para construir aplicações Ring, logo é importante entender o segundo para "desmistificar" a coisa toda. A documentação é bem abrangente, mas vai aqui um breve resumo na intenção de facilitar tua jornada.
A idéia base é criar aplicações usando apenas funções. Uma aplicação é uma função que converte um request em um response:
(defn hello-world [request]
{:status 200, :headers {}, :body "Hello World"})
Request é um mapa e a resposta é um mapa. Por vezes você vai encontrar coisas como a seguinte:
(defn hello-world [request]
(ring.util.response/response "Hello World"))
O primeiro reflexo pode ser pensar que um response é algo especial, tipo algum "objeto" de alguma determinada classe. Não se engane, ele gera exatamente o código acima.
Ok, temos a aplicação, e como ficam os middlewares? Um middleware nada mais é que uma função que chama sua função de "request". Um middleware que transforma tudo em ALL CAPS ficaria assim:
(defn upper-case-body [request]
(let [response (hello-world request)]
(update response :body clojure.string/upper-case)))
Excelente! Exceto pelo fato que está tudo muito amarrado entre si. Como hello-world
está cravada dentro do próprio middleware, eu não consigo reutilizá-lo para outros handlers. Comofas? Podemos reescrever ele usando uma composição de funções:
(defn wrap-upper-case-body [handler]
(fn [request] ;; nosso antigo `upper-case-body`
(let [response (handler request)]
(update response :body clojure.string/upper-case))))
Agora temos uma função que "envelopa" um handler e retorna ele dentro do middleware que queremos. Note que o wrap-*
é o padrão de nomenclatura adotado pelo Ring para este tipo de função "produtora" de middlewares. O wrap-json-body
não é muito diferente dessa acima não.
Existem certos handlers que são comuns a muitos tipos de aplicação. Mas veja só que interessante, tudo o que um wrap-*
faz é receber um handler e retornar um handler. Logo, poderíamos fazer um wrap-*
que encadeira todos estes!
(defn wrap-commons [handler]
(wrap-json-body (wrap-body-params handler)))
Que já explicaram, pode ser reescrito da seguinte forma:
(defn wrap-commons [handler]
(-> handler
(wrap-body-params)
(wrap-json-body)))
Ufa... bem melhor. Notou o que fizemos acima? Isso mesmo, nossa própria versão de wrap-defaults
- bem mais pobrezinha, diga-se de passagem.
No fim, veja que não tem nada de mágico aqui. Tudo que fizemos poderia ser feito em uma linguagem OO usando algum tipo de interface para handlers e outra para os construtores de middleware (o que deixo em aberto como exercício para o leitor). A única diferença é que com muito menos cerimônias ;)
Espero que com isso Ring, middlewares e principalmente composição de funções tenha ficado um pouquinho mais claro.