erlyweb and phased_vars IRC tutorial transcript
While I've been promising to publish an example erlyweb app, it's taking me a while to acquire the sufficient round tuits to do so. In the mean time, I gave a quick tutorial on IRC last night on how to use phased_vars. It's really informal, full of bad spelling, and probably not 100% accurate. But since there's a total dearth of erlyweb documentation, here it is:
[00:42] ketralnis_: So a request comes into yaws. Yaws takes that request, and looks at the appmods in yaws.conf to see what app gets that request
[00:43] ketralnis_: Yaws sees that, for this request, erlyweb is responsible. Erlyweb asks the "opaque" parameter (also in yaws.conf) to see what erlyweb app is responsible
[00:43] ketralnis_: Let's call it wrotit
[00:43] ketralnis_: So erlyweb does some initial (very minimal) processing, mostly wrapping up the request coming in from yaws in the yaws_arg
[00:44] ketralnis_: Then erlyweb passes control to wrotit_app_controller:hook/1, which it expects to return an {ewc}-type tuple
[00:45] ketralnis_: So in wrotit_app_controller, I ask erlyweb to determine what the best controller/view is for this request, by saying Ewc = erlyweb:get_initial_ewc({ewc, A1}).
[00:45] ketralnis_: (where A1 is the yaws_arg passed into wrotit_app_controller:hook/1)
[00:46] maddiin: looking at this files now, so far I can follow you.
[00:46] ketralnis_: So an {ewc}-tuple is the type of a set of tuples that are all basically shortcuts for {ewc, controller_module_name, view_controller_name, [Set,Of,Args]}
[00:47] ketralnis_: So erlyweb:get_initial_ewc looks at a request like /post/show/23, and turns it into {ewc, post_controller, post_view, [A, 23]} (where A is again the yaws_arg, which is passed to most controller functions for convenience)
[00:48] ketralnis_: Now, we don't want to just render that page, right? We want to render that page along with a sidebar and some other crap. So instead of returning just that {ewc}, we're going to return a special {ewc}-type tuples, which is the {phased}-type
[00:49] ketralnis_: The {phased} {ewc}-type tuple is a special tuple that is returned to erlyweb, which renders the result, and passes the entire rendered IOlist into a fun/3, which is the third item in the {phased,{ewc,whatever},fun/3} tuple
[00:49] ketralnis_: Is that making sense so far
[00:49] ketralnis_: ?
[00:49] maddiin: yes, so far I´m ok
[00:49] ketralnis_: The whole {phased} thing takes a sec to think about, or at least it did me. It's hard to see it unless you see the development process that ends in it
[00:50] ketralnis_: Okay. So the idea is that we return a tuple like {phased, {ewc, post, show, [A, "23"], fun/3} }
[00:50] ketralnis_: Erlyweb renders that {ewc}, and gets back something like "<h2>My name is bob</h2><p>...</p>" in one big iolist
[00:52] ketralnis_: Then erlyweb passes that whole iolist into the fun, which looks like fun(Ewc, RenderedPage, PhasedVars) -> whatever() end.
[00:52] ketralnis_: Ewc is just the {ewc, post, show ...} from before, and RenderedPage is that big iolist. I'll get to PhasedVars in a moment
[00:53] ketralnis_: So now let's peek into post_controller:show/2.
[00:54] ketralnis_: post_controller is the module that is called by anything that starts with /post/. get_initial_ewc decides that (you could override, in your hook/1)
[00:54] ketralnis_: Controller functions (those called by URL, like in /post/show, "show" is the controller function) are expected to return {ewc}-type tuples
[00:54] ketralnis_: Or (potentially deep) lists of them
[00:55] ketralnis_: So it may return, for instance, [ {data,post:title(Post)}, {ewc, post, comments, [A, Post]}, {ewc, post, blah, [A, Post]}]
[00:56] ketralnis_: Erlyweb takes that returned list, does any further processing, and passes it to post_view, as one list of arguments
[00:56] ketralnis_: As in the music-example that I gave on-list, if you saw that. That's the essence of the component system
[00:57] maddiin: yes, that was very helpful
[00:57] ketralnis_: so post_view:show/1 may look like show([Title, Comments, Blah]) and so on
[00:59] ketralnis_: However, sometimes we want to return information that we don't want passed into the view. Some {ewc}-type tuples do this already, like {ewr}, which is used as a redirect
[01:02] ketralnis_: So when {ewr} is "passed up" to a {phased} ewc, it's not rendered, the redirect is just sent. That might not make sense until you've seen it
[01:03] ketralnis_: But basically, if your hook/1 returns {phased, {ewc, post, show, [A, "23"]}, fun/3}, and post_controller:show/2 returns an {ewr, login, index}, the fun/3 will never be called, erlyweb just never runs it and sends the redirect
[01:04] maddiin: ok, that makes sense
[01:05] ketralnis_: So let's ignore phased_vars for now, and say that now you have your rendered /post/show/23 page, and you're passing it into that fun/3
[01:05] ketralnis_: For me, it looks like:
[01:06] ketralnis_: {phased, Ewc, fun(_Ewc, Data,_PhasedVars) -> {ewc, html_container, index, [A1,{data, Data}]} end}
[01:06] ketralnis_: That's the whole return from hook/1 for me
[01:06] maddiin: mine is the same, I followed your example from the list
[01:07] ketralnis_: So that fun/3 returns *another* {ewc} tuple for the html_container_controller:index/2 function, which is then rendered, passed the rendered "business logic" page (in this case, showing a post
[01:07] ketralnis_: And since you know the component system, you know how that maps into a sidebar or whatever else
[01:08] maddiin: yes
[01:08] ketralnis_: So another {ewc}-type tuple (beyond {ewr} and {phased} ones that I just described) is the {response}-type. This one allows you to attach headers, phased_vars, etc
[01:09] ketralnis_: It *must* be the first-level controller function (that is, the one referred to by the URL, not a sub-component)
[01:09] ketralnis_: And I'm looking for its definition, just one sec
[01:11] ketralnis_: The second element in the {response, Elems} is a list of values that ErlyWeb should return to Yaws verbatim, with the exception of the (optional) {body, Body} for HTML or {body, MimeType, Body} tuple and any of the ewr tuples listed above (ErlyWeb translates the latter into their redirect_local equivalents). If, in addition to returning standard Yaws tuples, you want ErlyWeb to render the response's body using the component's view, you can include the {body, Body} or {body, MimeType, Body} tuple in Elems. Body may be any single ewc or data tuple, or a list thereof. ErlyWeb renders the elements of Body, sends the result to the view function, and embeds the resulting iolist in an {html, Iolist} tuple prior to returning it to Yaws
[01:11] ketralnis_: To quote the documentation
[01:12] ketralnis_: That's old though, let me find a newer copy that has the phased_var stuff
[01:12] ketralnis_: But it gives you a general idea while I hunt down (or write) an example
[01:14] ketralnis_: Okay, looks like I'll have to write one real quick
[01:15] maddiin:
[01:17] ketralnis_: http://www.mail-archive.com/erlyweb@googlegroups.com/msg00132.html
[01:17] ketralnis_: See that
[01:18] ketralnis_: So the idea is that from the top-level controller function, you can return something like:
[01:18] maddiin: yes, I remember that from the title and other non component data topic
[01:18] ketralnis_: {response, [{phased_vars, [{title, "The title!"}], [{body, [{ewc, my_component1},{ewc,my_component2}]}]}
[01:19] ketralnis_: So then your controller-function is rendered, and everyone is happy. Meanwhile, based in {phased}-land, the fun/3 is waiting around....
[01:20] ketralnis_: So the fun/3 gets passed (1) the {ewc} itself from the request {ewc, post, show, [A, "23"]} (2) The entire rendered IOlist "<h2>My post!</h2>..." and (3) a proplist containing everything that was passed in the phased_vars bit of the {response}-tuple returned from the top-level controller function (the one referred by the URL)
[01:21] ketralnis_: That proplist looks like [{title,"The title!"}] in the example above, or [{title,"foo"}, {meta,"bar"}] in the one on the link above
[01:22] ketralnis_: The erlang in-built "lists" and "proplist" modules both contain useful functions for manipulating proplists
[01:23] maddiin: have to look at the proplist, I only know about the lists, but its getting clearer now
[01:23] ketralnis_: So you could do something like:
[01:24] ketralnis_: fun(_Ewc,RenderedPage,PropList) -> {ewc, html_container, index, [A, RenderedPage, _Title=proplists:get_value(title,PropList)]} end
[01:25] ketralnis_: Then html_container_controller:index/3 would have a head like index(A,RenderedPage,Title) -> [{data,Title},{data,RenderedPage}].
[01:25] ketralnis_: And a view like
[01:26] ketralnis_: <%@ index([Title, RenderedPage]) %><html><head><title><% Title %></title></head><body><% RenderedPage %></body></html>
[01:26] ketralnis_: Except with whitespace, sidebars, etc
[01:27] maddiin: let my try this
[01:27] ketralnis_: And of course SANITISED I left that part out, but it's really important. Literal data included on a page that you got from a user (like the title of their blog entry) should *aways* be sanitised, escaped, etc
[01:28] maddiin: yes, I know of that, <script>alert('doh-nuts')</script> :=)
[01:28] ketralnis_: The component system makes funding *where* to do that much easier. I have a module called "q" with things like q:h/1 and q:date/1 that escape those, with short module-name and function names so that i remember to type them )
[01:28] ketralnis_: RIght
[01:29] ketralnis_: q:h/1 escapes HTML, q:date/2 formats dates, q:link/2 makes links properly sanitised, etc
[01:29] ketralnis_: There's a copy of it in the tictactoe server that I'll release cleaned up realsoonnow
[01:29] maddiin: great
[01:31] ketralnis_: Whew. That was a lot of typing. But I hope it clarified things. I fear that understanding any of this requires listening to the arguments with Yariv to understand *why* it's there, which doesn't reek of good design
[01:31] maddiin: woohoo
[01:32] maddiin: have my title set
[01:32] ketralnis_: Yaaay
[01:33] maddiin: thank you so much, I have to read what you wrote twice, but this was another breakthrough in understanding after the component system
[01:34] ketralnis_: I hope I went into enough detail. I only understand anything after reading the code that actually does it, which is why I started way back at how yaws gets requests to erlyweb and whatnot
[01:34] ketralnis_: I guess i could have just started with the example and worked backwards far enough and skipped most of that text
[01:35] maddiin: yes, you gave me a good insight, example was not needed, I went through a lot experimenting
[01:37] maddiin: I have to read the code as well and learn more of erlang... but now I have a lot more motivation again
Btw, your comments textarea in this blog is ..uhm.. 20x30.
See you on IRC. :)
Posted by maddiin on December 24, 2007 at 11:23 PM PST #