Fork me on GitHub
#yada
<
2017-04-06
>
borkdude11:04:01

Is is possible to change the interceptor chain during a request? A context has an interceptor chain, so I think I can just call yada.handler/append-interceptor on it (although the argument is called res, the key in the resource and context is the same)

borkdude11:04:55

However, this doesn’t work for me

malcolmsparks11:04:59

No, or not without doing a subresource (which is a dynamic 'get out of jail' feature for anything super-dynamic)

malcolmsparks11:04:17

Pedestal has that feature, I haven't come across a big need

borkdude11:04:02

Ah, so changing the interceptor chain on the next context doesn’t affect anything. Then why does a context have an interceptor chain?

malcolmsparks11:04:06

Once the handler is created, the interceptors are fixed - this doesn't have to be the final design, and if you can argue a use-case then please do

malcolmsparks11:04:33

Hmm - not sure - it inherits all the handler entries

borkdude11:04:32

I already solved this using two interceptors. I thought it was clever to remove the second interceptor based on a condition, so I wouldn’t have to check the condition in the second interceptor, but anyway, that’s probably too clever. I’m fine with the current design, just wondered if it was possible like in Pedestal.

borkdude11:04:04

I never used something like this in Pedestal either. In the last couple of days I’ve been comparing Yada with Pedestal, but we settled on Yada now as it suits or use case the best. More batteries included. We need Swagger. Tried Pedestal-API but it’s very macro oriented and made passing components to our routes awkward.

borkdude11:04:06

What made me wonder about Pedestal vs Yada: Pedestal has enter + leave functions in the interceptor while in Yada an interceptor is just a function from context to context and the chain and everything after i/invoke-method in the chain works on the response if I understand correctly. A much simpler design which I like. But maybe it doesn’t matter that much.

dominicm11:04:53

Pedestals interceptors are all about adding/removing the next set of interceptors to use dynamically. It's how the router works for example.

malcolmsparks11:04:54

Yada is really a curated set of interceptors that combine to offer increasing alignment to the HTTP specs. Pedestal leaves HTTP compliance an an exercise for the programming, giving them the tools to do so. My own opinion is that developers are too busy to have to read HTTP specs, it's the wrong abstraction for them to be working at.

kurt-yagram12:04:58

> My own opinion is that developers are too busy to have to read HTTP specs, Very true, but the sad part is, they don't, so you get quite some wrongly implemented api's > it's the wrong abstraction for them to be working at. ... which yada solves: the dev shouldn't worry about the http spec. Yeah, I like yada a lot.

lmergen12:04:53

problem being, in my experience, the average developer couldn't care less about correct HTTP / REST behaviour, and is perfectly fine using URI's as commands, rather than resource locations

borkdude16:04:52

In our Ring based app we had this:

(defn no-cache
  [response]
  (-> response
      (resp/header
       "Cache-Control" "no-cache, max-age=0, must-revalidate, no-store")
      (resp/header
       "Pragma" "no-cache")
      (resp/header
       "Expires" “0”)))
How do I apply this in a Yada resource?

borkdude16:04:30

I need a Yada resource, because in the returned HTML I want to include a link which I retrieved using (yada/url-for ctx ::home) which I cannot do with a plain Ring handler

borkdude16:04:48

Also I want to tag this resource with an id

borkdude16:04:06

So basically I need a resource, but want to ensure the response is NEVER cached

borkdude16:04:33

I can apply a no-caching interceptor maybe

borkdude16:04:37

which I write myself

malcolmsparks16:04:59

In your response function, if you return the actual response (start with (:response ctx)) then yada will know that you're returning an actual response

malcolmsparks16:04:21

You can then set the headers explicitly and they'll be merged into the response headers

malcolmsparks16:04:11

The reason for this design is that in Liberator we had this problem of how Liberator might recognise the difference between a Ring response and a normal map. There are situations in yada where you want to return a normal map (it might be a JSON response), etc.

malcolmsparks16:04:43

So the response in ctx is actually a Clojure Record - and remains the same record type even when you assoc/merge/conj into it

malcolmsparks16:04:59

But, another valid approach is to code your own interceptor

borkdude16:04:18

Ah I was trying to do this:

:produces #{"application/html"}
:methods {:get (fn [ctx] {:headers … :body …})} 
which didn’t work, because it tried to serialize a clojure map to html there

malcolmsparks16:04:25

You can add your own data to a yada resource record - just make sure you namespace the keys

malcolmsparks16:04:16

@borkdude - yes, that's exactly the problem - how can yada know you mean a Ring response? Perhaps it could guess it from the keys, but really that's a nasty surprise lurking there

malcolmsparks16:04:42

so the compromise is to use Clojure records - it works well, because you can treat records more or less like maps

borkdude16:04:50

so if you don’t use :methods but only :response directly in resource it will work with a Ring response?

malcolmsparks16:04:59

fish out the response record from the request context and modify it as you like

borkdude16:04:09

I also tried that actually

malcolmsparks16:04:10

no - use :methods

malcolmsparks16:04:20

but each method has a response function

borkdude16:04:39

now I get it

malcolmsparks16:04:40

if you want this on every method, even OPTIONS, then write an interceptor

malcolmsparks16:04:53

but I guess you'll only want no-cache on GETs

malcolmsparks16:04:05

I don't think proxies will cache other methods anyway

borkdude16:04:50

so if you use a :response key+fn, it’s a Ring response, if you pass a fn it’s a function of ctx to data which Yada will then coerce to the right format

malcolmsparks16:04:50

if you use a :response key and fn, the fn will be called. If you return a normal map, it will be coerced to the negotiated content-type, if you want to be explicit about the response, you return a response record

malcolmsparks16:04:14

You do that by getting the response record from the request context (it's under the :response key) and modifying it as you like

malcolmsparks16:04:53

e.g. (assoc-in (:response ctx) [:headers "cache-control"] "no-cache")

borkdude16:04:17

I thought I did this, but maybe (merge (:response ctx) ring-resp) doesn’t return the right type. I’ll be back later.

malcolmsparks16:04:56

ah - I thought merge didn't affect the type. I know dissoc will change a record to a map

borkdude17:04:59

Seems to be the case:

(comment
  (type yada-resp) ;; yada.context.Response
  (type ring-resp) ;; clojure.lang.PersistentArrayMap
  (type (merge yada-resp ring-resp)) ;; yada.context.Response 
  )

borkdude17:04:03

I had some other mistake on my resource: “application/html” which caused the page to appear as a download, hehe 😖

danielcompton20:04:45

In bidi, what is the string that is matched against the regular expression? Is it just the segment or the whole path?

danielcompton20:04:54

e.g. [ [ "foo/" [ #"\d+" :id ] "/bar" ] :handler ]

danielcompton20:04:06

which would match the string foo/123/bar but not foo/abc/bar.

dominicm20:04:43

whole path afaik. I don't think bidi pays any mind to segments.

danielcompton20:04:34

foo/a123/bar doesn't match

danielcompton20:04:12

I'm just a bit confused as to what it's being passed

danielcompton20:04:08

as if I make the regex #"^\d+$" then it doesn't match foo/123/bar

danielcompton20:04:12

does bidi turn the entire route path vector into a regex to match against? and use capturing groups to get the values for each regex?

malcolmsparks20:04:23

In the example #"\d" only applies to :id

malcolmsparks20:04:00

If you want the whole pattern to be matched with a regex then use a regex for the pattern. But you'll lose the bidirectionality

malcolmsparks20:04:39

@dominicm bidi does apply regexes to segments.

dominicm20:04:08

Sorry, I meant, bidi doesn't pay any special mind to path segments (it's not aware). Or is that wrong?

malcolmsparks20:04:37

It is aware. Not sure what you mean

dominicm20:04:40

In the example, the regex only matches the part between the segments though.

dominicm20:04:52

bidi doesn't automatically split by segments afaik

malcolmsparks20:04:30

Whatever it matches gets captured in :id, then the matching continues

dominicm20:04:30

@malcolmsparks exactly! bidi doesn't break up the url by segment was my meaning. It matches the path like a string.

malcolmsparks20:04:45

If your regex is too greedy it will capture the /bar as well

malcolmsparks20:04:58

And then the overall pattern will fail

danielcompton20:04:23

Thanks, I think I understand

danielcompton20:04:28

So using #"^\d+$" would be roughly equivalent to trying to write a regex foo\/(^\d+$)\/bar?

danielcompton20:04:37

which would fail

dominicm21:04:13

I have been wondering if it would be cool to build a router on top of https://github.com/cgrand/regex

mpenet14:04:42

it would indeed!

dominicm21:04:40

Or even just plain regex like django does