Look at the source for the clojure.core with-open macro. In the repl: `(source with-open)`.
I think Gary is right. with-open does exactly what you need, I should have thought of that, and you should probably use it. But if you want to get your version working, trying to understand what the with-open macro is doing. Your implementation can be simpler because you only have one explicit binding. Essentially you'll create a let as a backquoted form and then splice in the explicit symbol from the user: `(let [~sym ...server-instance-or-uri...] ... ) marc On Sat, Mar 12, 2016 at 1:57 AM, Johan Haleby <johan.hal...@gmail.com> wrote: > > > On Wed, Mar 9, 2016 at 7:32 PM, Marc Limotte <mslimo...@gmail.com> wrote: > >> With the macro approach, they don't need to escape it. >> > > Do you know of any resources of where I can read up on this? I have the > macro working with an implicit "uri" generated but I don't know how to make > it explicit (i.e. defined by the user) the way you proposed. > > >> >> On Wed, Mar 9, 2016 at 12:52 PM, Johan Haleby <johan.hal...@gmail.com> >> wrote: >> >>> Thanks a lot for your support Marc, really appreciated. >>> >>> On Wed, Mar 9, 2016 at 5:33 PM, Marc Limotte <mslimo...@gmail.com> >>> wrote: >>> >>>> Yes, I was assuming the HTTP calls happen inside the with-fake-routes! >>>> block. >>>> >>>> I missed the part about the random port. I se 3 options for that: >>>> >>>> *Assign a port, rather than random* >>>> >>>> (with-fake-routes! 9999 ...) >>>> >>>> >>>> But then, of course, you have to worry about port already in use. >>>> >>>> *An atom* >>>> >>>> (def the-uri (atom nil)) >>>> (with-fake-routes! the-uri >>>> ... >>>> (http/get @the-uri "/x")) >>>> >>>> *A macro* >>>> >>>> A common convention in Clojure would be to pass it a symbol (e.g. `uri` >>>> that is bound by the macro), rather implicitly creating `uri`. >>>> >>>> (with-fake-routes! [uri option-server-instance] >>>> >>>> route-map >>>> >>>> (http/get uri "/x")) >>>> >>>> >>> Didn't know about this convention so thanks for the tip. But is your >>> snippet above actually working code or does the user need escape "uri" and " >>> option-server-instance" using a single-quotes, i.e. >>> >>> (with-fake-routes! [*'*uri *'*option-server-instance] ...) >>> >>> >>>> >>>> or, with a pre-defined server >>>> >>>> (def fake-server ...) >>>> (with-fake-routes! >>>> >>>> route-map >>>> >>>> (http/get (:uri fake-server) "/x")) >>>> >>>> >>>> marc >>>> >>>> >>>> >>>> On Wed, Mar 9, 2016 at 1:00 AM, Johan Haleby <johan.hal...@gmail.com> >>>> wrote: >>>> >>>>> >>>>> >>>>> 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. >>>>> >>>> >>>> -- >>>> 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. >>> >> >> -- >> 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. > -- 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.