More power, more configurable subsites

Hi Matt (and the whole web-devel list), Now that Warp is out, and now that the Hamlet decisions have been made, I can (finally) start focusing on yesod-core. Most of the changes we want are already made I think, there are only two open issues that I'm aware of: * Proper request body parsing[1], which is *almost* done. * Disabling sessions for subsites[2] On the second point, I want to broaden our scope: I want to make it possible to run a full WAI application as a subsite. I'll give my current use case: I just wrote a fully-featured wai-app-static application, which takes care of redirecting from "/foo/directory" to "/foo/directory/" and other such things. Now, let's say I have a directory named "my.folder" or a file named "file". In Yesod, the URL cleaning will remove the trailing slash from the former, and add the trailing slash to the latter. However, wai-app-static will do the exact opposite, resulting in an infinite redirect loop. One possible solution to this predicament is to hard-code /static as a special prefix, and not do URL cleaning. But this is a horrible, ugly, evil hack that makes unicorns cry and babies vomit (trust me, I have two kids, you don't want to know what happened hear after I added that commit). What we *really* want is to give subsites much more control over their execution. As the Yesod dispatch functions are written right now, that's not really possible: we do all path cleanup first and then start routing. So here is a possible proposal for how Yesod dispatch should work: * Break up the pathinfo (/static/directory/) into pieces (["static", "directory", ""], the empty string at the end indicates a trailing slash). * Dispatch to subsites only. In other words, we'll keep a list of subsite prefixes separate from other routes, and check those against the unmodified pieces. * If there are any matches, we strip out the relevant prefix from the pieces and pass off control to the subsite. * Subsites created with normal Yesod functions (mkYesodSub) will then go right back into the dispatch function, but now the specific settings for that subsite will be in place. This means that they can control how cleanup is done, whether sessions are on, etc. * You can also write your own subsite code directly and bypass Yesod's dispatch system. This will also make it possible to write a appToSubSite function so that plain WAI applications can be included directly. (I'm hoping that with the release of Warp, there will be more non-Yesod WAI apps out there.) I haven't looked at implementing this yet, but it doesn't sound too hard. I wanted to ask what you thought about how this would interact with your more modular subsite code already. One other thing that comes up as related: Aristid Breitkreuz emailed me and asked why Yesod is a typeclass and not a record type. I gave an answer[3], but I wouldn't mind other people looking into it as well. I'm starting to think about using the records approach on the YesodSubSite typeclass at least and see how that works out. But I have not given this much thought, and this is really an orthogonal- albeit related- question. Michael [1] http://wiki.yesodweb.com/Proper%20request%20body%20parsing [2] https://github.com/snoyberg/yesod/issues#issue/24 [3] http://wiki.yesodweb.com/Yesod%20as%20typeclass%20or%20record

Michael,
This all sounds great, especially the ability to use WAI apps as
subsites. Would this also mean yesod sites could be deployed as
subsites, since they're converted to WAI apps?
I don't see any major problems between the proposed routing changes
and the existing subsite implementation. My patch for dynamic
subsites allows routes to subsites to contain dynamic pieces, but as
long as the subsite dispatch remains independent of parsing the route
to the subsite, that detail should be transparent.
If I understand correctly, the new dispatch process would look like:
1) identify the subsite / parse the route to the subsite
2) global site preparation
3) dispatch to the subsite
3a) identify the subsite handler / parse the route
3b) subsite preparation
3c) dispatch to the subsite handler
I put the preparation steps after identification, so that preparation
could be run within the handler (with handlerRoute set). Is that what
you had in mind?
-matt
On Thu, Jan 20, 2011 at 10:51 PM, Michael Snoyman
Hi Matt (and the whole web-devel list),
Now that Warp is out, and now that the Hamlet decisions have been made, I can (finally) start focusing on yesod-core. Most of the changes we want are already made I think, there are only two open issues that I'm aware of:
* Proper request body parsing[1], which is *almost* done. * Disabling sessions for subsites[2]
On the second point, I want to broaden our scope: I want to make it possible to run a full WAI application as a subsite. I'll give my current use case: I just wrote a fully-featured wai-app-static application, which takes care of redirecting from "/foo/directory" to "/foo/directory/" and other such things. Now, let's say I have a directory named "my.folder" or a file named "file". In Yesod, the URL cleaning will remove the trailing slash from the former, and add the trailing slash to the latter. However, wai-app-static will do the exact opposite, resulting in an infinite redirect loop.
One possible solution to this predicament is to hard-code /static as a special prefix, and not do URL cleaning. But this is a horrible, ugly, evil hack that makes unicorns cry and babies vomit (trust me, I have two kids, you don't want to know what happened hear after I added that commit). What we *really* want is to give subsites much more control over their execution. As the Yesod dispatch functions are written right now, that's not really possible: we do all path cleanup first and then start routing.
So here is a possible proposal for how Yesod dispatch should work:
* Break up the pathinfo (/static/directory/) into pieces (["static", "directory", ""], the empty string at the end indicates a trailing slash). * Dispatch to subsites only. In other words, we'll keep a list of subsite prefixes separate from other routes, and check those against the unmodified pieces. * If there are any matches, we strip out the relevant prefix from the pieces and pass off control to the subsite. * Subsites created with normal Yesod functions (mkYesodSub) will then go right back into the dispatch function, but now the specific settings for that subsite will be in place. This means that they can control how cleanup is done, whether sessions are on, etc. * You can also write your own subsite code directly and bypass Yesod's dispatch system. This will also make it possible to write a appToSubSite function so that plain WAI applications can be included directly. (I'm hoping that with the release of Warp, there will be more non-Yesod WAI apps out there.)
I haven't looked at implementing this yet, but it doesn't sound too hard. I wanted to ask what you thought about how this would interact with your more modular subsite code already.
One other thing that comes up as related: Aristid Breitkreuz emailed me and asked why Yesod is a typeclass and not a record type. I gave an answer[3], but I wouldn't mind other people looking into it as well. I'm starting to think about using the records approach on the YesodSubSite typeclass at least and see how that works out. But I have not given this much thought, and this is really an orthogonal- albeit related- question.
Michael
[1] http://wiki.yesodweb.com/Proper%20request%20body%20parsing [2] https://github.com/snoyberg/yesod/issues#issue/24 [3] http://wiki.yesodweb.com/Yesod%20as%20typeclass%20or%20record

On Fri, Jan 21, 2011 at 11:25 PM, Matt Brown
Michael,
This all sounds great, especially the ability to use WAI apps as subsites. Would this also mean yesod sites could be deployed as subsites, since they're converted to WAI apps?
Yes, with a few caveats: * The parent Yesod app will not be able to use the child app's type-safe URLs. * There will be no automatic approot support like there is for proper subsites, since each application will have its own Yesod instance. * Likewise, there will be no defaultLayout sharing, or for that matter any other settings contained in the Yesod typeclass.
I don't see any major problems between the proposed routing changes and the existing subsite implementation. My patch for dynamic subsites allows routes to subsites to contain dynamic pieces, but as long as the subsite dispatch remains independent of parsing the route to the subsite, that detail should be transparent.
If I understand correctly, the new dispatch process would look like: 1) identify the subsite / parse the route to the subsite 2) global site preparation 3) dispatch to the subsite 3a) identify the subsite handler / parse the route 3b) subsite preparation 3c) dispatch to the subsite handler
I put the preparation steps after identification, so that preparation could be run within the handler (with handlerRoute set). Is that what you had in mind?
That's basically it, but I'm not sure what you mean by "global site preparation." The idea here is to do as little as possible before dispatching to the subsite, that way if a subsite does not want to deal with sessions, it doesn't involve that overhead. Michael

That's basically it, but I'm not sure what you mean by "global site preparation." The idea here is to do as little as possible before dispatching to the subsite, that way if a subsite does not want to deal with sessions, it doesn't involve that overhead.
I just meant it as a way to enable middleware for all subsites at once. It should still be configurable though, so the author can switch to a per-subsite setup. For example, it may be appropriate for the scaffolder to enable sessions globally, and let the author switch to per-subsite if/when performance becomes a concern. -matt

On Sat, Jan 22, 2011 at 8:34 PM, Matt Brown
That's basically it, but I'm not sure what you mean by "global site preparation." The idea here is to do as little as possible before dispatching to the subsite, that way if a subsite does not want to deal with sessions, it doesn't involve that overhead.
I just meant it as a way to enable middleware for all subsites at once. It should still be configurable though, so the author can switch to a per-subsite setup. For example, it may be appropriate for the scaffolder to enable sessions globally, and let the author switch to per-subsite if/when performance becomes a concern.
The basic approach I'm leaning towards is having a method in the Yesod typeclass (call it yesodRunner for now) which converts a Handler to an Application. This would be where middlewares are defined. We will keep the default behavior the same as in Yesod 0.6, but allow the ability to override this to provide a more slimmed-down experience. Then there will also be a yesodSubRunner method (I guess on YesodSubSite) which, by default, will use the yesodRunner method from the master site, but which can be overridden to allow custom behavior for subsites. This would be the control over customization in the hands of the subsite authors. The one downside I see is if there's a subsite which requires sessions, and a user turns off sessions for his/her master site, things will break. But I think that's an issue addressed best with proper documentation. Michael
participants (2)
-
Matt Brown
-
Michael Snoyman