Hi James, If I understand your pattern correctly, you assoc a :routes entry to each component that contains routes. Then your router component depends on these components, selecting those that have a :routes key, and call each :routes value function in turn until one return a non-nil Ring response.?
I think the two approaches share the same essential design. First, the db (or anything else) is injected via the closure's lexical scope, which I think is one of the nicest aspects of the component pattern (compared with alternative approaches listed here http://stuartsierra.com/2013/03/29/perils-of-dynamic-scope) The component-with-routes-to-try is 'marked' as such by having a :routes key. With my bidi-based design I similarly 'mark' a component, but with a protocol (modular.bidi.WebService). Essentially bidi works the same way as Compojure - rather than calling functions, it keeps trying to match patterns until it gets one that returns a non-nil map. Aside: The reason I mark components with a protocol is because I need the component to provide two maps - one from routes to keywords, the other from keywords to handlers. This is not a core bidi idea, but something extra I've built on top by extending bidi's protocols in modular.bidi, which has to do with wanting the option of url formation (from the keyword back to the path). I'm not yet sure if this is the right design. In summary, yes, I think that the endpoint-component approach would work very well with Modular and having a Compojure router component (together with the endpoint-component function/pattern) would be very valuable. Regards, Malcolm On Friday, 9 January 2015 16:34:05 UTC, James Reeves wrote: > > On 8 January 2015 at 15:10, Malcolm Sparks <mal...@juxt.pro <javascript:>> > wrote: >> >> The idea here is that you want individual components to be able to >> 'contribute' groups of routes. That way, you can avoid the common practice >> of having one big 'god' namespace containing all the Ring routes, which is >> then tightly coupled to all your Ring handlers. Instead, it's nice to have >> the flexibility to compose systems from different combinations of >> components. >> >> One implementation approach is to have a 'router' component that >> delegates to its dependencies. That way, routes can be 'plugged in' using >> dependency declarations. >> >> Currently I only implemented this with bidi, which I'm slightly biased >> towards (because I wrote it). My 'router' implementation is here: >> https://github.com/juxt/modular/blob/master/modules/bidi/src/modular/bidi.clj >> > > That's an interesting setup. Are you merging the data-driven bidi routes > together? > > If I may, let me run past you an idea I've been experimenting with that > has a similar goal. > > I've been using functions that take in a configuration map (in practice, a > component) and return a handler. I call these "endpoints" (highly original, > I know). > > (defn hello-endpoint [config] > (fn [request] > {:status 200 > :headers {"Content-Type" "text/plain"} > :body "Hello World"})) > > Wrapping endpoints in a component is relatively straightforward: > > https://github.com/weavejester/duct/blob/master/duct/src/duct/component/endpoint.clj > > In Duct, the template/framework I'm designing, I use the Compojure > mechanism of trying endpoints in turn until one returns a non-nil value. > However, one could quite easily write a routing mechanism that dispatches > on a context path, or a regular expression. > > This approach is pretty minimal, but has a few advantages: > > 1. It's not tied to a particular routing library > 2. It's a simple interface > 3. Configuration is passed down via a closure & destructuring > > As a more practical example, consider an endpoint that relies on a > database connection: > > (defn product-endpoint [{db :db}] > (routes > (GET "/" [] (list-products db) > (POST "/" [product] (add-product db product)) > (GET "/:id" [id] (find-product db id)))) > > When we come to write our system, we wrap the endpoint in a component, and > have it depend upon the database component: > > (defn new-system [config] > (-> (component/system-map > :db (database (:db config)) > :product (endpoint-component product-endpoint)) > (component/system-using > {:product [:db]})) > > (In the above example I've omitted the component that combines all the > endpoints together into a handler, as I think that's a more opinionated > component.) > > Do you think this "endpoint" approach could work in Modular? I like the > idea of having a standard way of defining a subset of routes that depend on > a configuration. > > - James > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.