erlyweb and phased_vars IRC tutorial transcript

05:23PM Dec 24, 2007 in category Erlang by David King

(Cross-posted to wrotit)

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

Comments[1]

Comments:

Hey David, great that you posted the conversation (tutorial :-]). I wanted to suggest this, because you wrote so much and its for sure helpful stuff for others, too.

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 #

Post a Comment:
Comments are closed for this entry.