On Wed, Mar 9, 2016 at 6:20 AM, Johan Haleby <johan.hal...@gmail.com> wrote:
> Thanks for your feedback, exactly what I wanted. > > On Tuesday, March 8, 2016 at 3:16:02 PM UTC+1, mlimotte wrote: >> >> I don't think you need a macro here. In any case, I'd avoid using a >> macro as late as possible. See how far you get with just functions, and >> then maybe at the end, add one macro if you absolutely need it to add just >> a touch of syntactic sugar. >> >> routes should clearly be some sort of data-structure, rather than >> side-effect setter functions. Maybe this: >> >> (with-fake-routes! >> optional-server-instance >> route-map) >> >> Hmm now that I come to think of it I don't see how this would actually work unless you also perform the HTTP request from inside the scope of with-fake-routes!, otherwise the server instance would be closed before you get the chance to make the request. Since you make an actual HTTP request you need access to the URI generated when starting the fake-server instance (at least if the port is chosen randomly). So either I suppose you would have to do like this (which requires a macro?): (with-fake-routes! {"/x" {:status 200 :content-type "application/json" :body (slurp (io/resource "my.json"))}} ; Actual HTTP request (http/get uri "/x")) where "uri" is created by the with-fake-routes! macro *or* we could return the generated fake-server. But if so with-fake-routes! cannot automatically close the fake-server instance since we need the instance to be alive when we make the call to the generated uri. I suppose it would have to look something like this: (let [fake-server (with-fake-routes! {"/x" {:status 200 :content-type "application/json" :body (slurp (io/resource "my.json"))}})] (http/get (:uri fake-server) "/x") (shutdown! fake-server)) If so I think that the second option is unnecessary since then you might just go with: (with-fake-routes! *required*-server-instance route-map) instead of having two options. But then we loose the niceness of having the server instance be automatically created and stopped for us? >> Where optional-server-instance, if it exists is, an object returned by ( >> fake-server/start!). If optional-server-instance is not passed in, then >> with-fake-routes! >> creates it's own and is free to call (shutdown!) on it automatically. >> And route-map is a Map of routes: >> > >> { >> "/x" >> {:status 200 :content-type "application/json" :body (slurp (io/resource >> "my.json"))} >> {:path "/y" :query {:q "something")}} >> {:status 200 :content-type "application/json" :body (slurp (io/resource >> "my2.json"))} >> } >> >> > +1. I'm gonna go for this option. > > >> >> Also, at the risk of scope creep, I could foresee wanting the response to >> be based on the input instead of just a static blob. So maybe the value of >> :body could be a string or a function of 1 arg, the route-- in your code >> test with (fn?). >> > > That's a good idea indeed. I've already thought about this for matching > the request. I'd like this to work: > > { > (fn [request] (= (:path request) "/x")) > {:status 200 :content-type "application/json" :body (slurp (io/resource > "my.json"))} > {:path "/y" :query {:q (fn [q] (clojure.string/starts-with? q "some"))}} > {:status 200 :content-type "application/json" :body (slurp (io/resource > "my2.json"))} > } > > Thanks a lot for your help and feedback! > > >> >> This gives you a single api, no macros, optional auto-server start/stop >> or explicit server management. >> >> marc >> >> >> On Tue, Mar 8, 2016 at 3:10 AM, Johan Haleby <johan....@gmail.com> wrote: >> >>> Hi, >>> >>> I've just committed an embryo of an open source project >>> <https://github.com/johanhaleby/fake-http> to fake http requests by >>> starting an actual (programmable) HTTP server. Currently the API looks like >>> this (which in my eyes doesn't look very Clojure idiomatic): >>> >>> (let [fake-server (fake-server/start!) >>> (fake-route! fake-server "/x" {:status 200 :content-type >>> "application/json" :body (slurp (io/resource "my.json"))}) >>> (fake-route! fake-server {:path "/y" :query {:q "something")}} >>> {:status 200 :content-type "application/json" :body (slurp (io/resource >>> "my2.json"))})] >>> ; Do actual HTTP request >>> (shutdown! fake-server)) >>> >>> >>> fake-server/start! starts the HTTP server on a free port (and thus have >>> side-effects) then you add routes to it by using fake-route!. The first >>> route just returns an HTTP response with status code 200 and content-type >>> "application/json" and the specified response body if a request is made >>> with path "/x". The second line also matches that a query parameter called >>> "q" must be equal to "something. In the end the server is stopped. >>> >>> I'm thinking of converting all of this into a macro that is used like >>> this: >>> >>> (with-fake-routes! >>> "/x" {:status 200 :content-type "application/json" :body (slurp >>> (io/resource "my.json"))} >>> {:path "/y" :query {:q "something")}} {:status 200 :content-type >>> "application/json" :body (slurp (io/resource "my2.json"))}) >>> >>> This looks better imho and it can automatically shutdown the webserver >>> afterwards but there are some potential problems. First of all, since >>> starting a webserver is (relatively) slow it you might want to do this once >>> for a number of tests. I'm thinking that perhaps as an alternative (both >>> options could be available) it could be possible to first start the >>> fake-server and then supply it to with-fake-routes! as an additional >>> parameter. Something like this: >>> >>> (with-fake-routes! >>> fake-server ; We pass the fake-server as the first argument in >>> order to have multiple tests sharing the same fake-server >>> "/x" {:status 200 :content-type "application/json" :body (slurp >>> (io/resource "my.json"))} >>> {:path "/y" :query {:q "something")}} {:status 200 :content-type >>> "application/json" :body (slurp (io/resource "my2.json"))}) >>> >>> If so you would be responsible for shutting it down just as in the >>> initial example. >>> >>> Another thing that concerns me a bit with the macro is that routes >>> doesn't compose. For example you can't define the route outside of the >>> with-fake-routes! >>> body and just supply it as an argument to the macro (or can you?). I.e. >>> I think it would be quite nice to be able to do something like this: >>> >>> (let [routes [["/x" {:status 200 :content-type "application/json" :body >>> (slurp (io/resource "my.json"))}] >>> [{:path "/y" :query {:q "something")}} {:status 200 >>> :content-type "application/json" :body (slurp (io/resource "my2.json"))}]]] >>> (with-fake-routes routes)) >>> >>> Would this be a good idea? Would it make sense to have overloaded >>> variants of the with-fake-routes! macro to accommodate this as well? >>> Should it be a macro in the first place? What do you think? >>> >>> Regards, >>> /Johan >>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "Clojure" group. >>> To post to this group, send email to clo...@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+u...@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+u...@googlegroups.com. >>> For more options, visit https://groups.google.com/d/optout. >>> >> >> -- > 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 a topic in the > Google Groups "Clojure" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/clojure/gieS5hQCUm4/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- 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.