GithubHelp home page GithubHelp logo

comidi's Introduction

comidi

A committee approach to defining Clojure HTTP routes.

Build Status

Comidi is a library containing utility functions and compojure-like syntax-sugar wrappers around the bidi web routing library. It aims to provide a way to define your web routes that takes advantage of the strengths of both bidi and compojure:

  • Route definitions are, at the end of the day, simple data structures (like bidi), so you can compose / introspect them.
  • Helper functions / macros for defining routes still provide the nice syntax of compojure; destructuring the request in a simple binding form, 'rendering' the response whether you define it as a string/map literal, a function reference, an inline body form, etc.

Quick Start

Clojars Project

(let [my-routes (context "/my-app/"
                    (routes
                        (GET "/foo/something" request
                            "foo!")
                        (POST ["/bar/" :bar] [bar]
                            (str "bar:" bar))
                        (PUT ["/baz/" [#".*" :rest]] request
                            (call-baz-fn request))
                        (ANY ["/bam/" [#"(bip|bap)" :rest]] request
                            {:orig-req request
                             :rest    (-> request :route-params :rest)))
      app        (-> (routes->handler my-routes)
                     wrap-with-my-middleware)]
   (add-ring-handler app))

Notable differences from compojure above:

  • use vectors to separate segments of a route, rather than using special syntax inside of a string (e.g. ["/bar/" :bar] instead of compojure's `"/bar/:bar")
  • use a nested vector with a regex (e.g. [".*" :rest]) to match a regex
  • context macro does not provide a binding form for request vars like compojure's does.

Other than those differences, the API should be very close to compojure's.

You can apply a Ring middlware to all the handlers at the leaves of a route using wrap-routes, which has behaviour analagous to its counterpart in compojure. Multiple middlewares can be applied to the same routes, with those applied later wrapped around those applied earlier. This allows you to create multiple routes wrapped with different middleware yet still combine them into one overarching route that can be introspected.

(let [my-routes ...
      my-singly-wrapped-routes (wrap-routes my-routes inner-middleware)
      my-doubly-wrapped-routes (wrap-routes my-singly-wrapped-routes outer-middleware)
      my-other-routes ...
      my-wrapped-other-routes (wrap-routes my-other-routes other-middleware)
      my-combined-routes (routes my-doubly-wrapped-routes my-wrapped-other-routes)]

What does Comidi do?

Comidi provides some macros and functions that are intended to feel very similar to the compojure routing macros / functions, but under the hood they construct, compose, and return bidi route trees rather than compojure handler functions.

This way, you can define your routes with almost exactly the same syntax you've been using (or port over a compojure app with minimal effort), but end up with an introspectable route tree data structure that you can do all sorts of cool things with before you wrap it as a ring handler.

Under the hood: comidi uses bidi to do all of the work for routing, and uses a few functions from compojure to maintain some of the nice syntax. Specifically, it uses compojure's route destructuring to bind local variables for parameters from the requests, and it uses compojure's "rendering" functions to allow you to define the implementation of your route flexibly (so, just like in compojure, your route definition can be a literal return value, a reference to a function, a call to a function, a String, etc.)

Comidi also provides a function called route-metadata. This function walks over your route tree and generates a metadata structure that gives you information about the all of the routes; e.g.:

(clojure.pprint/pprint
  (-> (route-metadata (routes
                        (GET "/foo" request
                          "foo!")
                        (PUT ["/bar/" :bar] [bar]
                          (str "bar: " bar))))
      :routes))
[{:route-id "foo", :path ["" "/foo"], :request-method :get}
 {:route-id "bar-:bar", :path ["" "/bar/" :bar], :request-method :put}]

Comidi also provides its own middleware function, wrap-with-route-metadata. If you use this middleware, your ring request map will be supplemented with two extra keys: :route-metadata, which gives you access to the metadata for all of the routes in your route tree, and :route-info, which tells you which of those routes the request matches. e.g.:

(clojure.pprint/pprint
  (let [my-routes (routes
                    (ANY "/foo" request
                      {:route-info (:route-info request)}))
        handler (-> my-routes
                    routes->handler
                    (wrap-with-route-metadata my-routes))]
    (:route-info (handler {:uri "/foo"}))))
{:route-id "foo", :path ["" "/foo"], :request-method :any}

Trapperkeeper / Metrics Integration

The trapperkeeper-comidi-metrics contains some middleware that will automatically generate and track metrics for all of the routes in your comidi/bidi route tree, as well as easy integration into trapperkeeper.

What's next?

  • API docs: looking into swagger integration. I could swear I found some bidi-swagger bindings somewhere a while back, but am not finding them at the moment. It might be possible to re-use some of the code from compojure-api because of the similarity between the comidi API and the compojure API.

  • You tell me! This is pre-1.0 and the API should still be considered fungible. If there's something you need that this library isn't doing, we can probably do it. Ping us or submit a PR.

Support

We use the Trapperkeeper project on JIRA for tickets on Comidi, although Github issues are welcome too.

comidi's People

Contributors

camlow325 avatar dankreek avatar jelinwils avatar jonathannewman avatar justinstoller avatar kevincorcoran avatar knbanderson avatar magisus avatar mruzicka avatar mwaggett avatar nmburgan avatar nwolfe avatar pcarlisle avatar puppetlabs-jenkins avatar rlinehan avatar scottyw avatar steveax avatar theshanx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

comidi's Issues

Test failures when upgrading Clojure from 1.8 to 1.9 (spec conformance)

Hi,

I'm trying to get PuppetDB in a suitable state for the next Debian stable release (Buster, Debian 10), which will be frozen in a few weeks. The puppetdb package depends on libcomidi-clojure, which can no longer be built from source, due to test issues:

dh_auto_test
(cd test && find . -name "*.clj" | \
	xargs --verbose clojure -cp /comidi-clojure-0.3.1/comidi.jar:/usr/share/java/clojure.jar:/usr/share/java/clj-time.jar:/usr/share/java/bidi.jar:/usr/share/java/compojure.jar:/usr/share/java/prismatic-schema.jar:/usr/share/java/kitchensink.jar)
clojure -cp /comidi-clojure-0.3.1/comidi.jar:/usr/share/java/clojure.jar:/usr/share/java/clj-time.jar:/usr/share/java/bidi.jar:/usr/share/java/compojure.jar:/usr/share/java/prismatic-schema.jar:/usr/share/java/kitchensink.jar ./puppetlabs/comidi_test.clj 
Exception in thread "main" clojure.lang.ExceptionInfo: Call to clojure.core/ns did not conform to spec:
In: [1] val: ((require [clojure.test :refer :all] [puppetlabs.comidi :as comidi :refer :all] [schema.test :as schema-test] [schema.core :as schema] [clojure.zip :as zip] [bidi.bidi :as bidi])) fails at: [:args] predicate: (cat :docstring (? string?) :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses),  Extra input
:clojure.spec/args  (puppetlabs.comidi-test (require [clojure.test :refer :all] [puppetlabs.comidi :as comidi :refer :all] [schema.test :as schema-test] [schema.core :as schema] [clojure.zip :as zip] [bidi.bidi :as bidi]))
 #:clojure.spec{:problems [{:path [:args], :reason "Extra input", :pred (cat :docstring (? string?) :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses), :val ((require [clojure.test :refer :all] [puppetlabs.comidi :as comidi :refer :all] [schema.test :as schema-test] [schema.core :as schema] [clojure.zip :as zip] [bidi.bidi :as bidi])), :via [], :in [1]}], :args (puppetlabs.comidi-test (require [clojure.test :refer :all] [puppetlabs.comidi :as comidi :refer :all] [schema.test :as schema-test] [schema.core :as schema] [clojure.zip :as zip] [bidi.bidi :as bidi]))}, compiling:(/comidi-clojure-0.3.1/test/./puppetlabs/comidi_test.clj:1:1)
	at clojure.lang.Compiler.load(Compiler.java:7442)
	at clojure.lang.Compiler.loadFile(Compiler.java:7368)
	at clojure.main$load_script.invokeStatic(main.clj:277)
	at clojure.main$script_opt.invokeStatic(main.clj:337)
	at clojure.main$script_opt.invoke(main.clj:332)
	at clojure.main$main.invokeStatic(main.clj:423)
	at clojure.main$main.doInvoke(main.clj:386)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:700)
	at clojure.main.main(main.java:37)
Caused by: clojure.lang.ExceptionInfo: Call to clojure.core/ns did not conform to spec:
In: [1] val: ((require [clojure.test :refer :all] [puppetlabs.comidi :as comidi :refer :all] [schema.test :as schema-test] [schema.core :as schema] [clojure.zip :as zip] [bidi.bidi :as bidi])) fails at: [:args] predicate: (cat :docstring (? string?) :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses),  Extra input
:clojure.spec/args  (puppetlabs.comidi-test (require [clojure.test :refer :all] [puppetlabs.comidi :as comidi :refer :all] [schema.test :as schema-test] [schema.core :as schema] [clojure.zip :as zip] [bidi.bidi :as bidi]))
 {:clojure.spec/problems [{:path [:args], :reason "Extra input", :pred (cat :docstring (? string?) :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses), :val ((require [clojure.test :refer :all] [puppetlabs.comidi :as comidi :refer :all] [schema.test :as schema-test] [schema.core :as schema] [clojure.zip :as zip] [bidi.bidi :as bidi])), :via [], :in [1]}], :clojure.spec/args (puppetlabs.comidi-test (require [clojure.test :refer :all] [puppetlabs.comidi :as comidi :refer :all] [schema.test :as schema-test] [schema.core :as schema] [clojure.zip :as zip] [bidi.bidi :as bidi]))}
	at clojure.core$ex_info.invokeStatic(core.clj:4725)
	at clojure.spec$macroexpand_check.invokeStatic(spec.clj:697)
	at clojure.spec$macroexpand_check.invoke(spec.clj:686)
	at clojure.lang.AFn.applyToHelper(AFn.java:156)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.lang.Var.applyTo(Var.java:700)
	at clojure.lang.Compiler.macroexpand1(Compiler.java:6816)
	at clojure.lang.Compiler.macroexpand(Compiler.java:6888)
	at clojure.lang.Compiler.eval(Compiler.java:6962)
	at clojure.lang.Compiler.load(Compiler.java:7430)
	... 9 more
debian/rules:32: recipe for target 'override_dh_auto_test' failed

It was reported in Debian#889125, and I've spent some time investigating the causes, leading me to narrow it down to the Clojure upgrade from 1.8.0-3 to 1.9.0~alpha15-1. I've documented all my findings in message #12 of that bug report, but you'll find below the Clojure specific parts (comidi, bidi).

I've tried upgrading comidi to the latest upstream release (0.3.2), which didn't change anything; I haven't learned about leiningen enough just yet but I've spotted the bidi upgrade, and tried to upgrade the debian package from 2.1.2 to 2.1.3 (given 4e5c91b, even if that seemed to be irrelevant at first), which led to no better results.

Maybe fabc6d8 and/or 9a5bfdf might help, if I understood what they are achieving exactly and how to translate that at the debian package level? But at least from the commit messages, they don't look like obvious fixes for the spec conformance issue we are seeing when trying to run tests?

Thanks for any help/guidance.

Season's greetings,
Cyril.

(route-metadata (routes)) fails validation

If you call routes without arguments, you cannot run route-metadata on the result, despite nothing filing validation up to that point:

(route-metadata (routes))

Unhandled clojure.lang.ExceptionInfo
   Input to breadth-route-metadata* does not match schema: [nil nil (named (not
   (puppetlabs.kitchensink.core/zipper? nil)) loc)]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.