Skip to content

Instantly share code, notes, and snippets.

@andrewhr
Created June 21, 2016 12:25
Show Gist options
  • Save andrewhr/4f94dda104ef561548410a3a72786cff to your computer and use it in GitHub Desktop.
Save andrewhr/4f94dda104ef561548410a3a72786cff to your computer and use it in GitHub Desktop.
Clojure, Ring & `wrap-defaults`

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment