mendicant-original / newman

Newman: a microframework for building email-based applications.
116 stars 11 forks source link

Server must handle multiple applications as implemented. #3

Closed guilherme closed 12 years ago

guilherme commented 12 years ago

Take a look at this gist: https://gist.github.com/1732428

I've thought about two solutions to accept multiple applications for one server:

I'll do some work on this later, but i wanted to share with you this concern, maybe you guys think on other solutions.

practicingruby commented 12 years ago

So in your gist you basically create a separate response for each incoming email. I was sort of thinking of applications originally as just being stackable components for building a single response for each incoming email, sort of like mixing several modules into a base class.

We have a hard limitation that affects the ability to have truly independent applications running within a single process: The Mail gem uses a global configuration system that effectively limits it (with hacky workarounds) to pulling from a single inbox. With that limitation in mind, the question becomes: does each application build its own response, or do all applications work together to produce a single response?

If we go the route of 'all applications share one response', then each application effectively could implement independent extensions as long as they didn't conflict with one another, but it'd be easy for them to collide if not designed carefully. The main reason I wanted multiple applications is actually not for folks to be able to have several different mail applications running in the same app, but instead so that you could have plugins that solve common problems for you (maybe error handling, help documentation, basic mailing list subscribe/unsuscribe functionality, etc). This makes them sort of similar to rack handlers in a way.

If we go the "all applications have their own response", it opens the door for more independence between the applications, but risks multiple emails being sent for the same request. I wonder what the advantage of this might be, given that we're locked to a single inbox per process. Do you have an example in mind?

I'm going to close this ticket for now because I don't have an immediate action plan for it, but I will tag it with the 'design' tag and keep an eye on discussions here. If we come up with a good solution to the problem, we can reopen it.

guilherme commented 12 years ago

Would be great if this could be extended with plugins for actions that are replicant, like subscribing, help.

Following the code reasoning passing the stack for process i could think of: introduce some "router" to recognize a Application which could handle this. And we could introduce some router for recognize the Action responsible for building the response for application, if none matched the default could be used.

Other thing that is weird is the fact of we could have many applications with the default response. Which one we should choose? What is the criteria for selecting the "Action" ? The action will share the same response ? Suppose we have two callbacks, one for "subject" other for "body" which one has precedence ? (The code just do 1 response, it does not process all stuff in one. It wouldn't possible for ex. 1 app -> add "Hello" to response body, 2 app -> add "World" to body). This router would be responsible to respond these questions.

Well, but this don't seem to be a simple solution and i think the problem (add re-utilizable actions) could be solved by other way like (hackish): https://gist.github.com/1734930

practicingruby commented 12 years ago

Your gist does point at the question of whether Application really needs to (poorly) reinvent the mixin concept :)

I'm going to let practical use cases drive the design on this framework, as opposed to trying to imagine the right way to make it extensible up front. But the points you brought up about precedence of callbacks is worth thinking about. Right now I pretty much am imagining that this bit of functionality can only be used for things that are order-independent and non-overlapping with one another. But because the system does not enforce that, it'd probably be easy for subtle and hard to track down bugs to be introduced.

I may end up locking the server down to one application and perhaps creating a convenient way to add/share behaviors, perhaps just layering some syntactic sugar on top of module mixins. I just need to think of a example application I can build in which these features will be useful. A help system or a mailing list sub/unsub thing might make sense and both are tools I want to add.

ericgj commented 12 years ago

Thanks for raising this issue Guilherme, it was one of my first questions looking at it too.

Re use cases, I am finding it hard to imagine where a single-response stack-of-apps would be that useful. If you want to modularize your routes, having separate apps doesn't seem like the best solution because of the hidden order-dependencies, although I guess possible.

OTOH, with a little work you could have something similar to Rack::Map, which seems like a clean way to do it - puts the routes all together in one place.

The other related question is whether the route-matchers should only run the first callback and not all matching. I think the least surprising behavior given how we are used to web frameworks working, is to run only the first.

Is there some kind of case for re-usable 'middleware' here? Perhaps bounce-back handling? Setting custom email headers? (Seems like a lot of the things Rack middleware get used for are not as relevant since we don't care about the performance; it's not a synchronous request-response.)

practicingruby commented 12 years ago

@ericgj: These comments are useful, but at the same time please try to stay grounded in specific, real use cases. Right now I feel that we're going off the deep end to discuss theoretical problems and that's not a good direction to head in.

practicingruby commented 12 years ago

@ericgj, @guilherme: If you are interested in exploring this topic further, please post a gist to this ticket that's use-case oriented.