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.

Reply via email to