I was locally reorganizing the ewgi files/projects and just realized that I wouldn't be able to upload them to my github repo without confusing any newcomers about what the official version/structure really is. So... I'd like to conduct a pool to check whether the changes I'm doing are compatible with the ewgi roadmap. The main goal with the following changes is to get all generic ewgi middleware to be integrated in the same place preventing everyone from reinventing the wheel and making it easier for new projects to get started (without having to cut/copy and paste code from mochiweb/smak/yaws/...). On the long run it would be pretty cool to have ports of all of these<http://rack.rubyforge.org/doc/Rack.html>ready to use. :)
At this point the ewgi_examples is mixing the "ewgi gateway server"'s startup logic and the example middleware. I suggest that this middleware be moved to the ewgi project so that new projects depend solely on ewgi (and not ewgi_examples).
The smak framework contains lots of useful pieces for actually creating something using ewgi. With that in mind I'd suggest that some of the modules also get moved to the ewgi project.
One additional thing I find unfriendly is that figuring out what ewgi middleware exists consists of opening up the files and checking if they're support code or actual middleware (both in smak and the ewgi_examples projects). :\
These are the changes I implementing: ewgi/ src/[ewgi.app, ewgi_api.erl, ewgi_application.erl, ewgi_test.erl, ewgi_testapp.erl] src/middleware/<middleware folder>/[middleware files] src/server_gateways/[ewgi_inets.erl, ewgi_mochiweb.erl, ewgi_yaws.erl, TODO:ewgi_misultin.erl] src/utils/[generic support modules moved from smak and renamed/prefixed with "ewgi_util_"] <- at this moment I have the modules ewgi_util_calendar.erl, ewgi_util_cookie.erl and ewgi_util_crypto.erl.
smak/ src/[smak.app, smak_test.erl, ...] src/middleware/<smak middleware folder>/[smak middleware files] <- if it had no particular smak dependencies it could be moved to ewgi/src/middleware src/utils/[support modules that are specific to smak]
ewgi_examples/ deps/ewgi/ [deps/mochiweb/] - if using the ewgi_mochiweb gateway [deps/smak/] - for smak based projects src/[...]
I'd rename the ewgi_examples project to ewgi_example_server as it would no longer contain ewgi middleware but an example of how to boot a ewgi web server and starting an ewgi-based project.
> At this point the ewgi_examples is mixing the "ewgi gateway > server"'s startup logic and the example middleware. > I suggest that this middleware be moved to the ewgi project so that > new projects depend solely on ewgi (and not ewgi_examples).
> The smak framework contains lots of useful pieces for actually > creating something using ewgi. With that in mind I'd suggest that > some of the modules also get moved to the ewgi project.
I agree. I thought for a long time that these projects should be separate for ideological reasons, but now it seems those reasons just make projects impractical.
> One additional thing I find unfriendly is that figuring out what > ewgi middleware exists consists of opening up the files and checking > if they're support code or actual middleware (both in smak and the > ewgi_examples projects). :\
> These are the changes I implementing: > ewgi/ > src/[ewgi.app, ewgi_api.erl, ewgi_application.erl, ewgi_test.erl, > ewgi_testapp.erl] > src/middleware/<middleware folder>/[middleware files] > src/server_gateways/[ewgi_inets.erl, ewgi_mochiweb.erl, > ewgi_yaws.erl, TODO:ewgi_misultin.erl] > src/utils/[generic support modules moved from smak and renamed/ > prefixed with "ewgi_util_"] <- at this moment I have the modules > ewgi_util_calendar.erl, ewgi_util_cookie.erl and ewgi_util_crypto.erl.
> smak/ > src/[smak.app, smak_test.erl, ...] > src/middleware/<smak middleware folder>/[smak middleware files] <- > if it had no particular smak dependencies it could be moved to ewgi/ > src/middleware > src/utils/[support modules that are specific to smak]
> ewgi_examples/ > deps/ewgi/ > [deps/mochiweb/] - if using the ewgi_mochiweb gateway > [deps/smak/] - for smak based projects > src/[...]
> I'd rename the ewgi_examples project to ewgi_example_server as it > would no longer contain ewgi middleware but an example of how to > boot a ewgi web server and starting an ewgi-based project.
Perhaps when you have finished reorganising your fork, we can merge the changes into the parent ewgi project.
Major changes: - created middleware folders to separate the ewgi middleware from the rest of the code; - created util folders for support modules used by more than one middleware (I argue that if it's used by a single middleware than it should be place in its folder); - migrated the middleware that was generic enough (as well as support modules) to the ewgi repository (that includes ewgi_deflate, ewgi_post, ewgi_session and ewgi_stream_file); - kept the index and to_upper middleware examples in the ewgi_examples repository - but renamed them to prevent name clashes; - upgraded the smak modules to reference the support modules that where moved (and renamed accordingly) to the ewgi repository; - started using Emakefiles for building ewgi_examples; - added a script to generate the .app files based on the ebin/*.beam files (questionable decision...); - added content to the ewgi_examples/README - with a bit discussing ewgi terminology (see if I got it right).
I'd like to migrate the remaining smak middleware (the authentication ones especially) to the ewgi project but I fear bloating the project by simply copying over all the required dependencies. Perhaps we should discuss what are the basic/minimal pieces that should go into ewgi.
Now, fist of all, I must say this is wow 0_o :) I love it :)
There's always a "however" lurking somewhere, of course :)
I disagree with definitions for middleware and applications:
Current version:
Ewgi Middleware:
- any function that receives an ewgi_context() and does
something with it. That's it. No further rules apply. :\
Ewgi Application:
- a 2 parameter function that receives an ewgi_context()
as its first argument and a parameter list as a second argument.
This convention/restriction from the generic "ewgi middleware"
described above defines a stable interface that developers can
count on when integrating 3rd party middleware.
In my opinion, the two are actually equivalent :) Since the latter is
just the former, only with options. I think the real power in
applications is chaining them. And there's currently no chaining
method specified.
Right now middleware can only be chained sequentially like this:
We could extend the existing proposal for middleware like this:
Ewgi Applicaton:
A parametrized middleware module that is initialized with a single
parameter, App. If the module modifies the request, it should pass the
modified request to this App. If the module modifies the response, it
should get the response from the app and modify it:
This will require slight modification to the chaining code, but will
help turn any middleware component into a chainable application with
just one or two lines of code.
> Major changes:
> - created middleware folders to separate the ewgi middleware from
> the rest of the code;
> - created util folders for support modules used by more than one
> middleware (I argue that if it's used by a single middleware than it
> should be place in its folder);
> - migrated the middleware that was generic enough (as well as
> support modules) to the ewgi repository (that includes ewgi_deflate,
> ewgi_post, ewgi_session and ewgi_stream_file);
> - kept the index and to_upper middleware examples in the
> ewgi_examples repository - but renamed them to prevent name clashes;
> - upgraded the smak modules to reference the support modules that
> where moved (and renamed accordingly) to the ewgi repository;
> - started using Emakefiles for building ewgi_examples;
> - added a script to generate the .app files based on the ebin/ > *.beam files (questionable decision...);
> - added content to the ewgi_examples/README - with a bit discussing
> ewgi terminology (see if I got it right).
> I'd like to migrate the remaining smak middleware (the
> authentication ones especially) to the ewgi project but I fear
> bloating the project by simply copying over all the required
> dependencies.
> Perhaps we should discuss what are the basic/minimal pieces that
> should go into ewgi.
> Now, fist of all, I must say this is wow 0_o :) I love it :)
> There's always a "however" lurking somewhere, of course :)
> I disagree with definitions for middleware and applications:
> Current version:
> Ewgi Middleware: > - any function that receives an ewgi_context() and does > something with it. That's it. No further rules apply. :\
> Ewgi Application: > - a 2 parameter function that receives an ewgi_context() > as its first argument and a parameter list as a second argument. > This convention/restriction from the generic "ewgi middleware" > described above defines a stable interface that developers can > count on when integrating 3rd party middleware.
> In my opinion, the two are actually equivalent :) Since the latter > is just the former, only with options. I think the real power in > applications is chaining them. And there's currently no chaining > method specified.
That's not strictly true. The ewgi specification tries to closely follow the idea behind Python's WSGI and Ruby's Rack in that there is no syntactic distinction between a middleware function and an application function. Are you speaking more generally about the example middleware and applications provided elsewhere?
> Right now middleware can only be chained sequentially like this:
> So, it's impossible to introduce a component that works on both the > request and the response (like caching, conditional get, ETags etc.)
I may be completely confused here, so bear with me. What about the following scenario (a middleware which routes to a particular application based on the value of an X-Boolean header):
conditional_middleware(AppTrue, AppFalse) -> F = fun(Ctx) -> % x_boolean_value/1 just returns true or false depending on the request header case x_boolean_value(Ctx) of true -> AppTrue(Ctx); false -> AppFalse(Ctx) end end, F.
> We could extend the existing proposal for middleware like this:
> Ewgi Applicaton: > A parametrized middleware module that is initialized with a single > parameter, App. If the module modifies the request, it should pass > the modified request to this App. If the module modifies the > response, it should get the response from the app and modify it:
I like the idea of being able to break down middleware pieces into smaller pieces of functionality that modify only the request or the response. Note that middleware can elect to modify both where necessary.
How does the above "conditional_middleware" example fit into this scheme? Also, are we talking about changing the specification here or simply creating some common idioms provided with the implementation(s) of the spec?
> On 22 Oct 2009, at 09:59, Dmitrii Dimandt wrote:
>> Now, fist of all, I must say this is wow 0_o :) I love it :)
>> There's always a "however" lurking somewhere, of course :)
>> I disagree with definitions for middleware and applications:
>> Current version:
>> Ewgi Middleware: >> - any function that receives an ewgi_context() and does >> something with it. That's it. No further rules apply. :\
>> Ewgi Application: >> - a 2 parameter function that receives an ewgi_context() >> as its first argument and a parameter list as a second argument. >> This convention/restriction from the generic "ewgi middleware" >> described above defines a stable interface that developers can >> count on when integrating 3rd party middleware.
>> In my opinion, the two are actually equivalent :) Since the latter >> is just the former, only with options. I think the real power in >> applications is chaining them. And there's currently no chaining >> method specified.
> That's not strictly true. The ewgi specification tries to closely > follow the idea behind Python's WSGI and Ruby's Rack in that there > is no syntactic distinction between a middleware function and an > application function. Are you speaking more generally about the > example middleware and applications provided elsewhere?
Well, Rack seems to impose the concept of a callable app (next in chain) from what I see in rack middleware examples.
>> So, it's impossible to introduce a component that works on both the >> request and the response (like caching, conditional get, ETags etc.)
> I may be completely confused here, so bear with me. What about the > following scenario (a middleware which routes to a particular > application based on the value of an X-Boolean header):
> conditional_middleware(AppTrue, AppFalse) -> > F = fun(Ctx) -> > % x_boolean_value/1 just returns true or false depending on > the request header > case x_boolean_value(Ctx) of > true -> > AppTrue(Ctx); > false -> > AppFalse(Ctx) > end > end, > F.
> When setting up the application, you would do:
> Mw = conditional_middleware(App1, App2).
If you're trying to create a middleware stack, then every single middleware in that stack would need to be passed a reference to the next app in the chain anyway. However, I see the problem in this case, when you need to choose a different app/stack based on the input...
>> We could extend the existing proposal for middleware like this:
>> Ewgi Applicaton: >> A parametrized middleware module that is initialized with a single >> parameter, App. If the module modifies the request, it should pass >> the modified request to this App. If the module modifies the >> response, it should get the response from the app and modify it:
> I like the idea of being able to break down middleware pieces into > smaller pieces of functionality that modify only the request or the > response.
That's true, that's usually the best way to go
> Note that middleware can elect to modify both where necessary.
Under the current scheme it can't :) Unless you specifically pass a reference to the next middleware/app in the chain. And you would have to do that for every component down the chain :)
Let's say, you have a caching middleware and a session middleware:
request -> | cache (if hit, return, if miss pass down) | session (get session cookie, retrieve data based on session, pass down) | web_app (spiderpig does what spiderpig does :)), pass up) | session (set cookie based on session data etc., pass up) | cache (caclulate CRC, place in cache etc., pass up) | response
How would you proceed in this case?
> How does the above "conditional_middleware" example fit into this > scheme? Also, are we talking about changing the specification here > or simply creating some common idioms provided with the > implementation(s) of the spec?
> On Oct 22, 2009, at 12:11 , Hunter Morris wrote:
>> On 22 Oct 2009, at 09:59, Dmitrii Dimandt wrote:
>>> Now, fist of all, I must say this is wow 0_o :) I love it :)
>>> There's always a "however" lurking somewhere, of course :)
>>> I disagree with definitions for middleware and applications:
>>> Current version:
>>> Ewgi Middleware: >>> - any function that receives an ewgi_context() and does >>> something with it. That's it. No further rules apply. :\
>>> Ewgi Application: >>> - a 2 parameter function that receives an ewgi_context() >>> as its first argument and a parameter list as a second argument. >>> This convention/restriction from the generic "ewgi middleware" >>> described above defines a stable interface that developers can >>> count on when integrating 3rd party middleware.
>>> In my opinion, the two are actually equivalent :) Since the latter >>> is just the former, only with options. I think the real power in >>> applications is chaining them. And there's currently no chaining >>> method specified.
>> That's not strictly true. The ewgi specification tries to closely >> follow the idea behind Python's WSGI and Ruby's Rack in that there >> is no syntactic distinction between a middleware function and an >> application function. Are you speaking more generally about the >> example middleware and applications provided elsewhere?
> Well, Rack seems to impose the concept of a callable app (next in > chain) from what I see in rack middleware examples.
Ok, my apologies. I was confused as to whether you were referring to the spec or the example pieces of middleware. I see your point now.
>>> So, it's impossible to introduce a component that works on both >>> the request and the response (like caching, conditional get, ETags >>> etc.)
>> I may be completely confused here, so bear with me. What about the >> following scenario (a middleware which routes to a particular >> application based on the value of an X-Boolean header):
>> conditional_middleware(AppTrue, AppFalse) -> >> F = fun(Ctx) -> >> % x_boolean_value/1 just returns true or false depending on >> the request header >> case x_boolean_value(Ctx) of >> true -> >> AppTrue(Ctx); >> false -> >> AppFalse(Ctx) >> end >> end, >> F.
>> When setting up the application, you would do:
>> Mw = conditional_middleware(App1, App2).
> If you're trying to create a middleware stack, then every single > middleware in that stack would need to be passed a reference to the > next app in the chain anyway. However, I see the problem in this > case, when you need to choose a different app/stack based on the > input...
So this kind of middleware may just be a separate idiom. Your example below (with modify_request/1 and modify_response/1) is probably a more common type of middleware. The specific problem I was thinking about was regarding routing. You may want a piece of middleware to choose an application based on the path, headers, etc.
>> Note that middleware can elect to modify both where necessary.
> Under the current scheme it can't :) Unless you specifically pass a > reference to the next middleware/app in the chain. And you would > have to do that for every component down the chain :)
> Let's say, you have a caching middleware and a session middleware:
> request -> > | > cache (if hit, return, if miss pass down) > | > session (get session cookie, retrieve data based on session, pass > down) > | > web_app (spiderpig does what spiderpig does :)), pass up) > | > session (set cookie based on session data etc., pass up) > | > cache (caclulate CRC, place in cache etc., pass up) > | > response
> How would you proceed in this case?
I think I see your point. You'd want something like this:
cache(App) -> F = fun(Ctx) -> case get_cached(Ctx) of Ctx1 -> Ctx1; {error, not_found} -> CtxToCache = App(Ctx), add_to_cache(CtxToCache), CtxToCache end end, F.
And similarly for the session middleware? Then you would set up your application as:
App = cache(session(web_app(Opts))).
Does that make any sense?
>> How does the above "conditional_middleware" example fit into this >> scheme? Also, are we talking about changing the specification here >> or simply creating some common idioms provided with the >> implementation(s) of the spec?
>>>> So, it's impossible to introduce a component that works on both >>>> the request and the response (like caching, conditional get, >>>> ETags etc.)
>>> I may be completely confused here, so bear with me. What about >>> the following scenario (a middleware which routes to a particular >>> application based on the value of an X-Boolean header):
>>> conditional_middleware(AppTrue, AppFalse) -> >>> F = fun(Ctx) -> >>> % x_boolean_value/1 just returns true or false depending on >>> the request header >>> case x_boolean_value(Ctx) of >>> true -> >>> AppTrue(Ctx); >>> false -> >>> AppFalse(Ctx) >>> end >>> end, >>> F.
>>> When setting up the application, you would do:
>>> Mw = conditional_middleware(App1, App2).
>> If you're trying to create a middleware stack, then every single >> middleware in that stack would need to be passed a reference to the >> next app in the chain anyway. However, I see the problem in this >> case, when you need to choose a different app/stack based on the >> input...
> So this kind of middleware may just be a separate idiom. Your > example below (with modify_request/1 and modify_response/1) is > probably a more common type of middleware. The specific problem I > was thinking about was regarding routing. You may want a piece of > middleware to choose an application based on the path, headers, etc.
Oh yeah, routing :) Mmmm.... Something like urlconf for Django.... :)
>>> Note that middleware can elect to modify both where necessary.
>> Under the current scheme it can't :) Unless you specifically pass a >> reference to the next middleware/app in the chain. And you would >> have to do that for every component down the chain :)
>> Let's say, you have a caching middleware and a session middleware:
>> request -> >> | >> cache (if hit, return, if miss pass down) >> | >> session (get session cookie, retrieve data based on session, pass >> down) >> | >> web_app (spiderpig does what spiderpig does :)), pass up) >> | >> session (set cookie based on session data etc., pass up) >> | >> cache (caclulate CRC, place in cache etc., pass up) >> | >> response
>> How would you proceed in this case?
> I think I see your point. You'd want something like this:
> cache(App) -> > F = fun(Ctx) -> > case get_cached(Ctx) of > Ctx1 -> > Ctx1; > {error, not_found} -> > CtxToCache = App(Ctx), > add_to_cache(CtxToCache), > CtxToCache > end > end, > F.
> And similarly for the session middleware? Then you would set up > your application as:
> App = cache(session(web_app(Opts))).
> Does that make any sense?
This setup doesn't allow either cache or session get to the request data ;) Since web_app gets called first, so both cache and session will only get the response data
>>> How does the above "conditional_middleware" example fit into this >>> scheme? Also, are we talking about changing the specification >>> here or simply creating some common idioms provided with the >>> implementation(s) of the spec?
> Oh yeah, routing :) Mmmm.... Something like urlconf for Django.... :)
Yep! If we're going to convince anybody to use ewgi for web framework projects (like webmachine, beepbeep, etc), they need to have some kind of URL routing!
>> I think I see your point. You'd want something like this:
>> cache(App) -> >> F = fun(Ctx) -> >> case get_cached(Ctx) of >> Ctx1 -> >> Ctx1; >> {error, not_found} -> >> CtxToCache = App(Ctx), >> add_to_cache(CtxToCache), >> CtxToCache >> end >> end, >> F.
>> And similarly for the session middleware? Then you would set up >> your application as:
>> App = cache(session(web_app(Opts))).
>> Does that make any sense?
> This setup doesn't allow either cache or session get to the request > data ;) Since web_app gets called first, so both cache and session > will only get the response data
In my example, web_app/1 is simply a setup function that *returns* an ewgi application. The same applies to session/1 and cache/1. Because they are each returning a function which then calls one of its arguments (except for web_app which does what spiderpig does), they are called in left-to-right order. If I simplify things vastly:
-module(test). -export([example/0]).
f(App) -> io:format("Setting up f...~n"), fun(Ctx) -> io:format("f is running (calling App)~n"), Ctx1 = App(Ctx), io:format("f is finished~n"), Ctx1 end.
g(App) -> io:format("Setting up g...~n"), fun(Ctx) -> io:format("g is running (calling App)~n"), Ctx1 = App(Ctx), io:format("g is finished~n"), Ctx1 end.
spiderpig(Opts) -> io:format("Setting up spiderpig...~n"), fun(Ctx) -> io:format("Running spiderpig with opts: ~p~n", [Opts]), Ctx end.
(emacs@tempe)2> test:example(). Setting up spiderpig... Setting up g... Setting up f... f is running (calling App) g is running (calling App) Running spiderpig with opts: [a,b,c] g is finished f is finished dummy_context
These middleware functions I've defined simply return closures which are used later in the app. If your middleware components are too complex to be in the body of an anonymous function (or you wanted to make use of hot code reloading):
> On 22 Oct 2009, at 12:39, Dmitrii Dimandt wrote:
>> Oh yeah, routing :) Mmmm.... Something like urlconf for Django.... :)
> Yep! If we're going to convince anybody to use ewgi for web > framework projects (like webmachine, beepbeep, etc), they need to > have some kind of URL routing!
>>> I think I see your point. You'd want something like this:
>>> cache(App) -> >>> F = fun(Ctx) -> >>> case get_cached(Ctx) of >>> Ctx1 -> >>> Ctx1; >>> {error, not_found} -> >>> CtxToCache = App(Ctx), >>> add_to_cache(CtxToCache), >>> CtxToCache >>> end >>> end, >>> F.
>>> And similarly for the session middleware? Then you would set up >>> your application as:
>>> App = cache(session(web_app(Opts))).
>>> Does that make any sense?
>> This setup doesn't allow either cache or session get to the request >> data ;) Since web_app gets called first, so both cache and session >> will only get the response data
> In my example, web_app/1 is simply a setup function that *returns* > an ewgi application. The same applies to session/1 and cache/1. > Because they are each returning a function which then calls one of > its arguments (except for web_app which does what spiderpig does), > they are called in left-to-right order. If I simplify things vastly:
> -module(test). > -export([example/0]).
> f(App) -> > io:format("Setting up f...~n"), > fun(Ctx) -> > io:format("f is running (calling App)~n"), > Ctx1 = App(Ctx), > io:format("f is finished~n"), > Ctx1 > end.
> g(App) -> > io:format("Setting up g...~n"), > fun(Ctx) -> > io:format("g is running (calling App)~n"), > Ctx1 = App(Ctx), > io:format("g is finished~n"), > Ctx1 > end.
> (emacs@tempe)2> test:example(). > Setting up spiderpig... > Setting up g... > Setting up f... > f is running (calling App) > g is running (calling App) > Running spiderpig with opts: [a,b,c] > g is finished > f is finished > dummy_context
> These middleware functions I've defined simply return closures which > are used later in the app. If your middleware components are too > complex to be in the body of an > anonymous function (or you wanted to make use of hot code reloading):
> I realise those are just toy examples, but does that clear anything > up?
It does... However, it seems somehow more complex from a middleware developer's point of view. Much more flexible, yes, but also more complex :) Or may be I'm not seeing it yet (I'm working on something like half a dozen of PHP files right now, so my vision is very blurry).
I don't see it right now, but it looks like this could be automated somehow. In the end I want to specify middleware to be run something like in http://docs.djangoproject.com/en/dev/topics/http/middleware/ — just throw in a list and let it handle itself :)
>> Hmmm... I didn't mean to say Id port it, but I guess I now have no >> choice :)))) But I'll definitely look into it, since I was going to >> anyway
> Excellent. I haven't had a chance to take a close look at > SimpleBridge, but I will hopefully be able to check it out at the > weekend.
--- start of my own wow moment --- Up to this point I had failed to realize the combining power of using functions that return: fun(Ctx) -> ... end
I mean... this piece of code:
> App = cache(session(web_app(Opts))).
Is just great! :) --- end of my own wow moment ---
Now for some code and then some bla bla on the end of the mail. :)
An alternative to writting:
> conditional_middleware(AppTrue, AppFalse) -> > F = fun(Ctx) -> > % x_boolean_value/1 just returns true or false depending on > the request header > case x_boolean_value(Ctx) of > true -> > AppTrue(Ctx); > false -> > AppFalse(Ctx) > end > end, > F.
is this:
> conditional_middleware(Ctx, [AppTrue, AppFalse]) -> > % x_boolean_value/1 just returns true or false depending on the request > header > case x_boolean_value(Ctx) of > true -> > AppTrue(Ctx); > false -> > AppFalse(Ctx) > end.
+ this:
> F = ewgi_application:mfa_mw(?EMAIL_MODULE, conditional_middleware, > [AppTrue, AppFalse]).
As for Dmitrii's example code I'd pseudo-whack it like this**:
> run(Ctx, [WebAppOpts, NextApp]) -> > spiderpig doing its thing.
---------------- **bla bla about the code you just saw ---------------- I argue that this code is: - verbose; - not easily "chainable"; - ugly; - damn ugly! :|
But despite that... it's: - configurable without touching the original middleware; - not-onion like!!! - you're not bound to return to the middleware again - you have full control over the order in which the middleware gets called (useful for 2 pieces of middleware that you want to be run in the same order over the request and the response) - generic***
***I argue that the "cache" above is more generic than this one:
> cache(App) -> > F = fun(Ctx) -> > case get_cached(Ctx) of > Ctx1 -> > Ctx1; > {error, not_found} -> > CtxToCache = App(Ctx), > add_to_cache(CtxToCache), > CtxToCache > end > end, > F.
because here: Ctx1 = get_cached(Ctx) the cache would be responsible for fetching the data and "rendering/processing" it, while on the "cache module" above: Ctx1 = CacheHitApp(get_cached(Ctx)) the cache module would be responsible solely for fetching the cached data, while the "render/processing" operation is handled by CacheHitApp - a configurable/external App.
---------------- bla bla about chaining middleware ----------------
I believe Dmitrii's quest for an easy way to chain middleware is a worthy one and that Hunter's suggestion fits the bill quite nicelly. And now for the "lurking however" ;) -> that chain can only be built when we're dealing with relatively simple middleware. It's hard to keep away from middleware that does execution flow control across Apps and when we get to that there's no simple solution in sight (from here at least).
---------------- bla bla about "application" and "middleware" ----------------
I now disagree with the definition old-Davide proposed about ewgi's "middleware" and "application". :)
Today (2009-10-22) I argue that:
- it makes more sense to call "Application" to the 1-arity functions that receives the ewgi_context() and return another ewgi_context() AND can be freely chained with other Apps like Hunter's example showed. So many outstanding features make it worthy of a name. :)
- the "middleware" name can be left to any code that operates on ewgi_context();
- module:run(EwgiCtx, OptionList) can be just an unnamed recommendation for defining a middleware function that is directly callable AND that can safely be wrapped by module ewgi_application's functions when we want to turn it into an "application" for combining it with other apps
In old-Davide's defense: it was late and he didn't know what he was writting about. :)
> I believe Dmitrii's quest for an easy way to chain middleware is a > worthy one and that Hunter's suggestion fits the bill quite nicelly. > And now for the "lurking however" ;) -> that chain can only be built > when we're dealing with relatively simple middleware. It's hard to > keep away from middleware that does execution flow control across > Apps and when we get to that there's no simple solution in sight > (from here at least).
I think perhaps it would be useful to create a list of desired middleware components that we could use:
As we build up a toolkit of middleware components, we will be able to see where to make useful higher level abstractions. I agree that chaining middleware components is extremely verbose at the moment.
It might be useful to statically define a middleware stack with a list of tuples and fold over that, chaining them together. Something like this:
> I now disagree with the definition old-Davide proposed about ewgi's > "middleware" and "application". :)
> Today (2009-10-22) I argue that: > • it makes more sense to call "Application" to the 1-arity > functions that receives the ewgi_context() and return another > ewgi_context() AND can be freely chained with other Apps like > Hunter's example showed. > So many outstanding features make it worthy of a name. :) > • the "middleware" name can be left to any code that operates on > ewgi_context();
> • module:run(EwgiCtx, OptionList) can be just an unnamed > recommendation for defining a middleware function that is directly > callable AND that can safely be wrapped by module ewgi_application's > functions when we want to turn it into an "application" for > combining it with other apps > In old-Davide's defense: it was late and he didn't know what he was > writting about. :)
That makes more sense to me, too. Generally, all middleware are applications, but not vice-versa. The crucial difference seems to be that middleware eventually rely on some other component to either modify the request or response (depending on where in the chain).
Meanwhile, I'm going to start merging Davide's structural changes into my branches and do some other cleanup unless anybody else has any objections. I think the server implementations could use some refactoring.
I won't be able to provide much feedback on work related to "wiring ewgi middleware" as I've taken a *way* different approach to wiring stuff together. It's not ewgi-specific so it's hard to "export/share" what I'm working without biasing/poisoning :) the ewgi project. Still, various building blocks are still ewgi worthy and I'll make the effort to refactor those for use by "regular" ewgi users. :)
I now disagree with the definition old-Davide proposed about ewgi's
>> Today (2009-10-22) I argue that: >> • it makes more sense to call "Application" to the 1-arity >> functions that receives the ewgi_context() and return another ewgi_context() >> AND can be freely chained with other Apps like Hunter's example showed. >> So many outstanding features make it worthy of a name. :) >> • the "middleware" name can be left to any code that operates on >> ewgi_context();
>> • module:run(EwgiCtx, OptionList) can be just an unnamed >> recommendation for defining a middleware function that is directly callable >> AND that can safely be wrapped by module ewgi_application's functions when >> we want to turn it into an "application" for combining it with other apps >> In old-Davide's defense: it was late and he didn't know what he was >> writting about. :)
> That makes more sense to me, too. Generally, all middleware are > applications, but not vice-versa. The crucial difference seems to be that > middleware eventually rely on some other component to either modify the > request or response (depending on where in the chain).
I guess you meant to say: "Generally, all applications (1 arity functions, receiving ewgi_context(), ...) are middleware (N/arity functions, receiving ewgi_context(),...)". Ping me back on this and I'll update the ewgi_examples/README, or better yet... migrate this info to ewgi/README. When time allows that is...
Meanwhile, I'm going to start merging Davide's structural changes into my
> branches and do some other cleanup unless anybody else has any objections. > I think the server implementations could use some refactoring.
No objections. :) One thing regarding the server gateways: can I drop the usage of parametrized modules? I see no benefits and it's getting in the way. :/