liberator

2015-08-19T13:39:07.000038Z

Hi all, I'm trying to use Bidi with liberator and Enlive and have a small problem with circular dependencies... Basically I want to use bidi.bidi/path-for to generate a route definition in one of my resources - but doing so will introduce a circular dependency as path-for needs access to my routes but I can't do this because my routes depend on my resources and my resources depend on my pages (enlive templates). These are currently split into three namespaces, so i can't really use a declare here... How do people normally factor their routes/resources/pages to avoid this?

2015-08-19T13:40:50.000040Z

It seems this circular dependency is implied for routes to be bidirectional, was wondering if bidi could possibly augment the request with the routes map

ordnungswidrig 2015-08-19T13:41:23.000042Z

One way is to bind your routes to a future and use that from within your handlers.

2015-08-19T13:41:50.000043Z

yeah I did think about using an atom or promise or something

2015-08-19T13:41:56.000044Z

I guess a promise would be best

ordnungswidrig 2015-08-19T13:43:33.000045Z

Oh, yes, I meant promise.

ordnungswidrig 2015-08-19T13:43:43.000046Z

I, personally, use component and co-dependencies from the modular lilbs: https://github.com/juxt/modular.co-dependency/

ordnungswidrig 2015-08-19T13:44:29.000048Z

It allows you to specify deps and co-deps in your system (I have a routes-provider component). The co-deps are resolved via deref and allow circular deps.

ordnungswidrig 2015-08-19T13:45:09.000049Z

> The penalty for not understanding this point is days spent figuring out why you can't see a co-dependency's data. It's because you're still trying to access this data within the start phase of your component. Don't do this. Defer the lookup until after the entire start phase of your system has completed.

ordnungswidrig 2015-08-19T13:45:12.000050Z

lol

2015-08-19T13:46:19.000051Z

interesting I guess components would help

ordnungswidrig 2015-08-19T13:47:49.000052Z

regarding the promise pattern: https://github.com/juxt/bidi/blob/master/doc/patterns.md

2015-08-19T13:48:06.000054Z

ahh now thats exactly what I was looking for

ordnungswidrig 2015-08-19T13:50:14.000055Z

With bidi you can use tagged routes, so you only need to pass around a promis that contains the routes

ordnungswidrig 2015-08-19T13:50:53.000056Z

So instead of promising the handlers, promise the routes

2015-08-19T13:51:34.000057Z

yeah I was thinking I'd promise routes

ordnungswidrig 2015-08-19T13:53:10.000058Z

Or you can bind your routes to the request, at :routes and should be able to avoid promises at all. *thinking*

2015-08-19T13:53:43.000060Z

That was what I was refering to earlier -- not sure how to do that though

2015-08-19T13:54:15.000061Z

I guess I could put a middleware in that would bind them after the routes were loaded

ordnungswidrig 2015-08-19T13:55:56.000062Z

Yes like that.

ordnungswidrig 2015-08-19T13:56:24.000063Z

(defn wrap-routes [h routes] (fn [req] (h (assoc req :routes routes))))

ordnungswidrig 2015-08-19T13:56:32.000064Z

I like that.

2015-08-19T13:57:02.000065Z

yeah its pretty simple - but means every page will need to have the parameter passed to it

2015-08-19T13:57:10.000066Z

not a big deal though

ordnungswidrig 2015-08-19T13:57:28.000067Z

You can apply wrap-routes very „top level"

2015-08-19T13:58:33.000068Z

yeah I know -- just each resource will need to destructure the routes from the ctx and either do the path-for or pass the routes to the enlive templates to do it

ordnungswidrig 2015-08-19T14:00:40.000069Z

I see. You can always use a dynamic var if that bugs you. And a function that accesses it.

(def :^private :^dynamic current-routes nil)
(defn path-for [& args]
  (apply bidi/path-for current-routes args))
;; and a middleware
(defn wrap-bind-routes [h routes]
  (fn [req] (binding [current-routes routes] (h req))))

ordnungswidrig 2015-08-19T14:02:40.000072Z

like that.

ordnungswidrig 2015-08-19T14:03:26.000073Z

path-for is a little suggar enabling (path-for ::user :id „some-user“) and you can omit the routes altogether :simple_smile:

ordnungswidrig 2015-08-19T14:04:17.000074Z

Not sure, however, how this would compose for nested route definitions when you have multiple subsystem that offer routes which you would mount together.

2015-08-19T14:12:23.000075Z

yeah I tend to avoid dynamic vars unless I really need them

ordnungswidrig 2015-08-19T14:15:06.000076Z

In this case, when you consider the routes a global thing (like you would with the promises) they come in handy. But testing can be a pain.

2015-08-19T14:17:04.000077Z

true

2015-08-19T14:19:00.000078Z

might do that after all - as in this case they're not going to change post initialisation... Thanks for talking this through with me... it was useful

malcolmsparks 2015-08-19T14:33:39.000079Z

@rickmoynihan: this circular dependency problem comes up occasionally, that's what co-dependency tries to help with - in this case the circular dependency is hard to get round, it's at the very core of the web's design - URIs identify resources, resources produces representations, representations should contain URIs, which identify resources ....

2015-08-19T14:34:23.000080Z

@malcolmsparks: yeah I realise its basically because bidi is bidirectional :simple_smile:

malcolmsparks 2015-08-19T14:34:31.000081Z

if you're into hypermedia APIs that is, and that's what bidi is really built to support

2015-08-19T14:34:32.000082Z

that implies circular dependency

2015-08-19T14:35:39.000083Z

co-dependency looks interesting -- but seems like overkill for just this -- though I have been looking at introducing component at some point

malcolmsparks 2015-08-19T14:35:44.000084Z

fwiw, I use co-dependency so that components that contain routable resources can also capture, via lexical scope, the overall 'routes' that each resource might need to produce representations that contain URIs

malcolmsparks 2015-08-19T14:36:39.000085Z

@rickmoynihan: ah, I'd only recommend co-dependency if you're already using component, if you aren't then use another approach like a promise

malcolmsparks 2015-08-19T14:37:02.000086Z

co-dependency is designed to workaround the fact that component enforces an ADT

2015-08-19T14:37:05.000087Z

yeah - I think I'm just going to throw it in a middleware in the request map

malcolmsparks 2015-08-19T14:37:23.000088Z

yep, that'll work (if it's a promise and you deref it of course)

malcolmsparks 2015-08-19T14:37:58.000089Z

I actually use a notation for derefables (promises, futures, refs), a star prefix

malcolmsparks 2015-08-19T14:38:02.000090Z

like *routes

malcolmsparks 2015-08-19T14:38:17.000091Z

so I remember to deref: @*routes

2015-08-19T14:38:26.000092Z

nice idea

malcolmsparks 2015-08-19T14:38:35.000093Z

it's an idea from @jarohen , but he uses a !

2015-08-19T14:38:46.000095Z

! is too overloaded

malcolmsparks 2015-08-19T14:39:03.000096Z

yeah, I don't think deref'ing is related to side-effects

2015-08-19T14:39:11.000097Z

ditto

2015-08-19T14:41:03.000098Z

@malcolmsparks: was meaning to ask you -- I recently looked at yada which looks really quite nice.... I especially like the liberator style + swagger... however I eventually chose liberator because it seemed like yada was still in flux and is less mature... Is that a fair assessment? Should I have chosen yada yet? :simple_smile:

malcolmsparks 2015-08-19T14:42:51.000099Z

oh yes, that's right. yada is very much a work-in-progress - it isn't ready for production use

malcolmsparks 2015-08-19T14:43:01.000100Z

liberator is much more mature

ordnungswidrig 2015-08-19T14:43:02.000101Z

I suggest use a function path-for which uses and derefs the global promise of the routes.

malcolmsparks 2015-08-19T14:43:24.000102Z

@ordnungswidrig: that's a nice idea

malcolmsparks 2015-08-19T14:43:55.000103Z

one thing to bear in mind is that bidi's path-for can be a little slow (compared to dispatch, which has been heavily tuned)

malcolmsparks 2015-08-19T14:44:46.000104Z

I've been talking with a co-contributor about creating an index, but until then I suggest you memoize if performance is critical

malcolmsparks 2015-08-19T14:45:03.000105Z

it's probably not a big deal, just bear in mind if you generate loads of uris

malcolmsparks 2015-08-19T14:45:23.000106Z

bidi must walk its tree each time - but we could easily create an index to speed it up, just haven't got round to it yet

2015-08-19T14:45:57.000107Z

@malcolmsparks: well I'll keep an eye on yada

2015-08-19T14:46:20.000108Z

I'd really like swagger support in liberator - has anyone done that yet?

ordnungswidrig 2015-08-19T14:46:23.000109Z

(declare routes)
(defn path-for [tag & args] (apply bidi/path-for @routes tag args))
(defresource foo [] :handle-ok (fn [ctx] (path-for ::bar)))
(defresource bar [] :handle-ok (fn [ctx] (path-for ::foo)))
(def routes (promise [/ [[„this-is-foo“ (tag foo ::foo)]
                         [„this-is-bar“ (tag bar ::bar)]]]))
…like that

ordnungswidrig 2015-08-19T14:46:55.000113Z

@rickmoynihan: I’ve seen people doing it but I don’t know of a general solution

ordnungswidrig 2015-08-19T14:47:39.000114Z

@malcolmsparks: yes, I think memoize would be the way to go. the promise contains a static route definition in the end.

malcolmsparks 2015-08-19T14:52:44.000115Z

the problem with doing swagger in liberator, generally speaking, is that liberator is based around boolean answers to questions - true, true, false, true, false, etc..

malcolmsparks 2015-08-19T14:52:54.000116Z

it's hard to build a swagger data model from this

ordnungswidrig 2015-08-19T14:53:15.000117Z

you can use a swagger data model, though, and use that to declare your liberator resources.

malcolmsparks 2015-08-19T14:53:36.000119Z

you mean go from swagger to liberator resource models? yes, that's true

malcolmsparks 2015-08-19T14:53:51.000120Z

but I think swagger is a poor choice for your source model

ordnungswidrig 2015-08-19T14:54:02.000121Z

or use your own data model and derive swagger and the liberator resource declaration from it.

malcolmsparks 2015-08-19T14:54:49.000122Z

yes, that would be better

malcolmsparks 2015-08-19T14:55:12.000123Z

yada is trying to help those who really can't be bothered to do all that just to get swagger output

malcolmsparks 2015-08-19T14:55:23.000124Z

(they shouldn't be using swagger at all of course, it's very un RESTful 😉

2015-08-19T14:57:18.000125Z

lol - well I don't care for swagger as such... more just that I want the automatic doc generation and test ui

2015-08-19T14:57:24.000126Z

I've not looked into swagger too much to know the details

2015-08-19T15:00:03.000128Z

@ordnungswidrig: FYI you can't use promise like that (its a arity 0) - I think we actually need a delay

ordnungswidrig 2015-08-19T15:01:08.000129Z

@rickmoynihan: oh, sorry, yes. that should’ve been (def routes (promise)) and (deliver routes […])

2015-08-19T15:02:03.000130Z

@ordnungswidrig: Or just (def routes (delay [ ... ]))

ordnungswidrig 2015-08-19T15:02:27.000131Z

@rickmoynihan: yes, if you can make sure it’s not derefed early :simple_smile:

ordnungswidrig 2015-08-19T15:02:38.000132Z

well, the promise would hang in that case.

ordnungswidrig 2015-08-19T15:03:54.000133Z

we totally need some documentation on the options.

ordnungswidrig 2015-08-19T15:04:25.000134Z

@malcolmsparks: shall we dump some of the ideas to bidi’s docs?

2015-08-19T15:05:04.000135Z

@ordnungswidrig: yeah it would be useful - bidi's main selling point is moot without having a solution to this

2015-08-19T15:15:01.000137Z

Does bidi have anything for assembling query string parameters? Or do I just need to str them on myself?

malcolmsparks 2015-08-19T15:32:17.000138Z

no, use str

ordnungswidrig 2015-08-19T16:42:42.000139Z

str is not enough, you need to properly urlencode the keys and values

malcolmsparks 2015-08-19T19:44:03.000141Z

yes, and interpose with &, I've got a function somewhere that does that