Gmail Calendar Documents Reader Web more »
Recently Visited Groups | Help | Sign in
Google Groups Home
IMPORTANT: Breaking changes in 0.11 branch: tons of new features!
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  7 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
David Zülke  
View profile  
 More options Jan 25 2007, 7:28 am
From: David Zülke <d...@bitxtender.com>
Date: Thu, 25 Jan 2007 13:28:30 +0100
Local: Thurs, Jan 25 2007 7:28 am
Subject: [Agavi-Users] IMPORTANT: Breaking changes in 0.11 branch: tons of new features!
Hi guys,

revision 1568 (http://trac.agavi.org/changeset/1568) saw the merge of  
the david-execution_flow branch to the 0.11 branch. This means some  
things will break, and it means many things have improved.

Before I begin, let me apologize for promising that there wouldn't be  
any more breaking changes after 0.11RC1. It was found out that the  
way things were, we couldn't implement caching (http://trac.agavi.org/
ticket/78, coming to SVN today!), so I figured I should just throw  
out rendering filters (http://trac.agavi.org/ticket/377) which were  
the primary reason caching wouldn't be possible, and while doing  
that, it became clear that I could remodel the execution flow  
entirely, with minimal to no BC breaks, away from the hacky Action  
Stack implementation to a system where we have a container that fully  
encapsulates the execution of an action, without any possibility of  
the "outer world" breaking things inside this container (http://
trac.agavi.org/ticket/373, and http://trac.agavi.org/ticket/290 which  
was originally scheduled for 2.0).

That all went very well, and it even became possible to use  
decorators within slots, and slots inside the content template,  
without decorators. We now had a very modern, very advanced, and were  
forward compatible execution flow model, but the templating system  
all of a sudden seemed horribly archaic in comparison. After some  
pondering, I finally had the idea to re-do the templating system so  
it works just like the decorators, but with an infinite number of  
layers, like an onion or a russian doll (http://trac.agavi.org/ticket/
287, also originally scheduled for 2.0). As a result, existing  
templates would work without modification, only the API in the views  
would change.

We also discovered that while our validation system worked very well  
for protecting code from malicious or non-validating request  
parameters, files, cookies, http headers etc didn't get filtered that  
way. Thus, we introduced request data holders that are now passed to  
the actions and hold all request data: parameters, files, cookies,  
headers etc (http://trac.agavi.org/ticket/389). While doing that, it  
became clear how awfully crappy the current file handling was. getFile
() will now return an object on which you can call getSize() to get  
the size, or move() to move the uploaded file (http://trac.agavi.org/
ticket/391). These two changes should result in only minimal breaking  
changes and can mostly be adapted to by some search and replace work.

Also, let me stress that that's it for now. No more planned features,  
everyone's happy. There are some issues and minor bugs to clean up,  
but besides that, I'm sure we won't see any more breakage from now  
on. Also because neither I nor anyone else wants that. Really. Again,  
my apologies.

Now let's move on to the actual changes.

First, the execution flow.

Until now, execution was done using Controller::forward(). A forward  
put the information about the action to run on the global action  
stack and then started running through the filters etc. Each time  
some code wanted to get, say, the action name, it had to pull the  
last item from the stack and read the information from that. As you  
can probably imagine, that was very prone to errors and totally  
messed things up when forwarding, for instance, since the information  
about what was going on (the action name, module name, action  
instance, request data etc) was on the stack, not encapsulated within  
the execution. This decoupling and indirect way of accessing the  
current state of execution was rather ugly.

Enter execution containers.
Controller::forward() is gone now, and each container encapsulates  
all information necessary to run an action: module name, action name,  
request information, the output type (yes, the output type is _not_  
global anymore!), the response, information about the view etc. When  
execute() is called, the entire execution happens fully isolated,  
like a black box. This also brings a slight performance improvement  
for the general execution process.

As a consequence, you obviously cannot use $actionStack->getLastEntry
()->getActionName() etc anymore. Instead, Views and Actions are given  
the container instance as the first argument to the initialize()  
method. It is then available via $this->getContainer(). Note that in  
Views, $this->getResponse() is a shortcut for $this->getContainer()-
 >getResponse(). So for example to grab the micro timestamp at which  
the current action started execution, do $this->getContainer()-
 >getMicrotime().

Also, filters have changed due to this. Filter execute() methods now  
get the filter chain as the first argument, and the container as the  
second. To proceed to the next filter in the chain, you must call  
$filterChain->execute($container), i.e. hand on the container you got  
as the second argument. Again, the response for the container is  
available from $container->getResponse().

Also, for forwarding and for setting slots, you use an execution  
container instance now, so to forward to EggAction in module Spam,  
you do return $this->container->createExecutionContainer('Spam',  
'Egg'); Likewise, to set a slot, you do setSlot('name', $container);  
(I'll get to slots later)
An important note here. As I mentioned, each execution container also  
has it's own output type. This is rather useful since you can run a  
slot with a different output type and have it produce, say, JSON data  
you use in a script tag in the page header. Unfortunately, the  
standard way to create an execution container is the  
createExecutionContainer method in the Controller, which will use the  
default output type. That's why each container has such a method too,  
which calls the controller's method, but with the same output type as  
that controller - basically, the container creates a "brother" of  
itself.
createExecutionContainer() optionally accepts a RequestDataHolder  
object as the third argument to pass additional information such as  
parameters, files, cookies to the forwarded action or the slot, and  
the name of an output type as the fourth argument.

When you define a forward, this information is stored in the current  
execution container. Once that finished running, it will execute this  
next container, and return THAT CONTAINER's RESPONSE. That means if  
you set a cookie or a redirect in the container and then forward,  
that information gets lost. This is usually the desired behavior, but  
there might be situations where you don't want that to happen.  
Because of that, there's also a global response in $controller-
 >getGlobalResponse(). There are circumstances when you should use  
that one, like when setting a cookie from a routing callback or so.

Also, redirection is now done via the response by calling $response-
 >redirect('targeturl'); Since you don't want &amp; there, remember  
to pass array('separator' => '&') as the third argument to  
Routing::gen(). WebResponse::redirect also accepts a second parameter  
for an HTTP status code other than the default 302.

Again, you have to make a wise decision between local container's  
response and the global response. Remember, only a local container's  
response will be included in caches. Use it whenever possible.

Other than that, this change shouldn't affect actions and/or views.  
Yes, rendering filters were removed, but I don't think anyone ever  
uesed these anyway. Likely since there was absolutely no use case for  
them, all they did was cause unnecessary overhead.

Next: the new template architecture.

This is a rather big change.
Until now, we had a content template, and a decorator template. A  
decorator template could also have slots, but the content template  
couldn't, and a slot couldn't use a decorator template itself. Per-
locale templates were only possible for the content template, not for  
the decorator. You could only have these two layers.  Both content  
templates any layers had to use the same renderer with the same  
configuration. Templates couldn't come from a database. And so on.  
Clearly, that was poorly implemented.

Now, all of the above works. And it's not even poorly implemented ;)

First, the layering.
Instead of setTemplate and setDecoratorTemplate, layers are now used.  
To mimic the old behavior, a view would have a layer called "content"  
with the content template (e.g. "IndexSuccess"), and another layer  
who's name doesn't really matter (let's call it "decorator") with the  
template "Master".
Now what happens is that the first layer is rendered, and it's result  
is available to the next layer as a slot. The name of the slot is the  
name of that layer. Here, we used "content" because it doesn't only  
make sense, but actually produces results similar to the old system,  
where the content template was always available as "content" in the  
decorator.
As you probably have guessed, layers are executed in the order they  
are defined. Of course, you can re-arrange them at runtime, remove  
them, add new ones at arbitrary locations and so on. So let's have a  
look at the code:

     $l1 = $this->createLayer('AgaviFileTemplateLayer', 'content');
     $l1->setTemplate('IndexSuccess');
     $this->appendLayer($l1);
     $l2 = $this->createLayer('AgaviFileTemplateLayer', 'decorator');
     $l2->setTemplate('Master');
     $this->appendLayer($l2);

Yes, you are absolutely right. This code is HIDEOUS. You don't want  
to do that in every view, and even with a base view (more on that  
later), it's awfully ugly.

BUT!

You can configure these layouts very easily, and as a result, you  
don't have to write that code anymore, instead you just do

     $this->loadLayout();

to load a layout (no name passed here, so it will use the default  
one). I'll explain this in more detail in a minute.

But first, let's look at slots and renderers.

First, slots. You can ...

read more »


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
David Zülke  
View profile  
 More options Jan 25 2007, 8:26 am
From: David Zülke <d...@bitxtender.com>
Date: Thu, 25 Jan 2007 14:26:58 +0100
Local: Thurs, Jan 25 2007 8:26 am
Subject: Re: [Agavi-Users] IMPORTANT: Breaking changes in 0.11 branch: tons of new features!
It is Response::setRedirect(), not redirect(), sorry.

Also, I forgot to mention that you cannot extract slots anymore, and  
that slots and template attributes cannot share a variable. That  
means slots won't be available in $template anymore, the default  
variable name is now $slots. All still configurable using "var_name",  
"slot_var_name" and "extract_vars" parameters of the renderer

Am 25.01.2007 um 13:28 schrieb David Zülke:

...

read more »


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Discussion subject changed to "[Agavi-Dev] IMPORTANT: Breaking changes in 0.11 branch: tons of new features!" by David Zülke
David Zülke  
View profile  
 More options Jan 25 2007, 8:28 am
From: David Zülke <d...@bitxtender.com>
Date: Thu, 25 Jan 2007 14:28:17 +0100
Local: Thurs, Jan 25 2007 8:28 am
Subject: Re: [Agavi-Users] [Agavi-Dev] IMPORTANT: Breaking changes in 0.11 branch: tons of new features!
Argh... "slots_var_name"

Am 25.01.2007 um 14:26 schrieb David Zülke:

...

read more »


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Discussion subject changed to "IMPORTANT: Breaking changes in 0.11 branch: tons ofnew features!" by Van Daele, Koen
Van Daele, Koen  
View profile  
 More options Jan 26 2007, 4:01 am
From: "Van Daele, Koen " <koen.vanda...@rwo.vlaanderen.be>
Date: Fri, 26 Jan 2007 10:01:27 +0100
Local: Fri, Jan 26 2007 4:01 am
Subject: Re: [Agavi-Users] IMPORTANT: Breaking changes in 0.11 branch: tons ofnew features!
Hello David (and others),

Thanks for all the hard work. Seems like there are some new cool things to play with. When I have a chance, I'll update to the latest revision and give it a try. Escecially the view/templating improvements seem to be just what I need.

Keep up the good work.

Greetings,
Koen

...

read more »


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Discussion subject changed to "Advanced Layout/Layers example (was: Re: IMPORTANT: Breaking changes in 0.11 branch: tons of new features!)" by David Zülke
David Zülke  
View profile  
 More options Jan 29 2007, 8:02 am
From: David Zülke <d...@bitxtender.com>
Date: Mon, 29 Jan 2007 14:02:21 +0100
Local: Mon, Jan 29 2007 8:02 am
Subject: [Agavi-Users] Advanced Layout/Layers example (was: Re: IMPORTANT: Breaking changes in 0.11 branch: tons of new features!)
Hi guys,

I thought I'd give you a little example of what the new template  
layer system can do.

We participated in the PHP Throwdown (http://www.phpthrowdown.com)  
and built an IRC bot using Agavi. It's called "Chuckwalla", contains  
a fully fledged IRC library that doesn't suck, has live logs, cool  
web interfaces etc and will be open sourced soon.

One feature is the live logs. You click a channel, and it uses Ajax  
to refresh the contents, so you can follow the discussion in a  
channel. So for the first load, we need a full document, and for  
subsequent XMLHttpRequest, we just want the new messages.

Here is the layout's configuration for output type "html":
<layout name="standard">
        <layers>
                <layer name="content" class="AgaviFileTemplateLayer" />
                <layer name="decorator" class="AgaviFileTemplateLayer">
                        <parameter name="template">Master</parameter>
                </layer>
        </layers>
</layout>

Let's assume this is our decorator template:
<html>
   <head><title>logs!</title></head>
   <body>
<?php echo $slots['content']; ?>
   </body>
</html>

And here is the content template (LiveSuccess.php):
<div id="roomTitle"><?php echo $t['topic']; ?></div>
<table border="0" cellspacing="0" cellpadding="0">
        <tbody>
<?php foreach($t['messages'] as $message): ?>
                <tr>
                        <td class="name"><?php echo $message->getNick()->getNick(); ?></td>
                        <td class="message"><?php echo $message->getMessage(); ?></td>
                        <td class="time"><?php echo $message->getMessageDate(); ?></td>
                </tr>
<?php endforeach; ?>
        </tbody>
</table>

Now we also have an output type for the AJAX calls, called "json".  
It's configured like this:
<layout name="standard">
        <layers>
                <layer name="content" class="AgaviFileTemplateLayer" />
        </layers>
</layout>

But now we have a problem: If an AJAX call is made, we get back the  
entire inner content template, complete with the <divs> and the  
<table>, but we only want the <tbody> to append to the current table  
(remember, tables can have multiple <tbody> elements).

One approach would be to simply have two templates. The other  
approach would be to split them up and include() the inner portion in  
the non-ajax version, and set the templates differently per output type.

However, there also is a third approach. Right now, we have these  
layers:
+------------------------+
| <html> decorator       |
| +--------------------+ |
| | <table> content    | |
| | with <tbody>, <tr> | |
| +--------------------+ |
| </html>                |
+------------------------+

What we can do now is split up the template like we would with the  
include() solution:
+------------------------+
| <html> decorator       |
| +--------------------+ |
| | <table> wrapper    | |
| | +----------------+ | |
| | | <tbody> inner  | | |
| | | content        | | |
| | +----------------+ | |
| | </table>           | |
| +--------------------+ |
| </html>                |
+------------------------+

First, the "wrapper" template (LiveSuccess.wrapper.php):
<div id="roomTitle"><?php echo $t['topic']; ?></div>
<table border="0" cellspacing="0" cellpadding="0">
<?php echo $slots['inner']; ?>
</table>

And the actual "inner" content template (LiveSuccess.php):
<tbody>
<?php foreach($t['messages'] as $message): ?>
        <tr>
                <td class="name"><?php echo $message->getNick()->getNick(); ?></td>
                <td class="message"><?php echo $message->getMessage(); ?></td>
                <td class="time"><?php echo $message->getMessageDate(); ?></td>
        </tr>
<?php endforeach; ?>
</tbody>

For Ajax, everything is fine now. We'll get the <tbody> content. But  
for output type "html", the result will be this:
<html>
   <head><title>logs!</title></head>
   <body>
<tbody>
        <tr>
                <td class="name">John McClane</td>
                <td class="message">Yippie-kay-yay motherfucker</td>
                <td class="time">03:27</td>
        </tr>
</tbody>
   </body>
</html>

What we need now is insert the wrapper IN BETWEEN "decorator" and  
"content" layer.

Remember that the decorator template expects to output $slots
['content']. Hence, we actually have to modify the "content" layer to  
display the wrapper template (LiveSuccess.wrapper.php), and then  
prepend another "inner" layer (LiveSuccess.php) to the layer list. We  
do it like this, in the view:

public function executeHtml(AgaviRequestDataHolder $rd)
{
        // remember: first, you should have a base view and call a method on  
the parent to load the layout instead of doing it here.
        // second, loadLayout() would be enough since "standard" is the  
default layer
        $this->loadLayout('standard');

        // we get the "content" layer and change the template to the wrapper
        $c = $this->getLayer('content');
        $c->setTemplate('LiveSuccess.wrapper');

        // and then prepend the actual "inner" content template to the list
        $i = $this->prependLayer($this->createLayer
('AgaviFileTemplateLayer', 'inner'));
        $i->setTemplate('LiveSuccess');

}

And that's it! Now we have the desired result:
<html>
   <head><title>logs!</title></head>
   <body>
<div id="roomTitle"><?php echo $t['topic']; ?></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
        <tr>
                <td class="name">John McClane</td>
                <td class="message">Yippie-kay-yay motherfucker</td>
                <td class="time">03:27</td>
        </tr>
</tbody>
</table>
   </body>
</html>

IMPORTANT: Some advice regarding base views. In the original email, I  
recommended that you always have a base view you extend from where  
execute() throws an exception. This view would also have base methods  
you can call that load layers, maybe set dynamic slots, or do stuff  
like:
$this->setAttribute('_contentType', $this->container->getOutputType()-
 >getParameter('Content-Type'));

However, there is a problem here. The whole point of having execute()  
throw an exception is that if there is a request using, say, ajax,  
that sets the output type to, say, "json" because of a routing rule  
like this:
<route pattern="^XMLHttpRequest$" source="_SERVER
[HTTP_X_REQUESTED_WITH]" stop="false" output_type="json" />

And you then implement a base executeJson() in the base view... all  
of your views DO serve the json output type, and the normal execute()  
is never called, not even for actions/views that don't implement ajax  
features and should thus get an exception!

To solve that problem, name these base methods differently. I suggest  
you call them "setupHtml", "setupJson" and so on, and then call them  
using $this->setupHtml($rd); etc inside your concrete view's  
executeHtml() method.

Again, if there are any questions, let me know.

Cheers,

David

_______________________________________________
users mailing list
us...@lists.agavi.org
http://lists.agavi.org/mailman/listinfo/users


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
David Zülke  
View profile  
 More options Jan 30 2007, 4:32 am
From: David Zülke <d...@bitxtender.com>
Date: Tue, 30 Jan 2007 10:32:08 +0100
Local: Tues, Jan 30 2007 4:32 am
Subject: Re: [Agavi-Users] Advanced Layout/Layers example (was: Re: IMPORTANT: Breaking changes in 0.11 branch: tons of new features!)
As of http://trac.agavi.org/changeset/1609, the output of the inner  
layer is available via the variable $inner in templates. Also  
available are $container for the current execution container and  
$view for the template's view. Like with factories, the names of  
these assigns ("inner", "container" and "view") can be configured  
using the "assigns" parameter of a renderer.

Since the wrapping template now does not have to know the inner  
layer's name anymore, the example can be simplified quite a bit.

Master.php:
<html>
        <head><title>logs!</title></head>
        <body>
<?php echo $inner; ?>
        </body>
</html>

LiveSuccess.php:
<tbody>
<?php foreach($t['messages'] as $message): ?>
        <tr>
                <td class="name"><?php echo $message->getNick()->getNick(); ?></td>
                <td class="message"><?php echo $message->getMessage(); ?></td>
                <td class="time"><?php echo $message->getMessageDate(); ?></td>
        </tr>
<?php endforeach; ?>
</tbody>

LiveSuccess.wrapper.php:
<div id="roomTitle"><?php echo $t['topic']; ?></div>
<table border="0" cellspacing="0" cellpadding="0">
<?php echo $inner; ?>
</table>

And the code that makes it happen:
public function executeHtml(AgaviRequestDataHolder $rd)
{
        // remember: first, you should have a base view and call a method on
the parent to load the layout instead of doing it here.
        // second, loadLayout() would be enough since "standard" is the
default layout
        $this->loadLayout('standard');

        // now we insert the wrapper template between the decorator and the  
actual content layer
        $i = $this->appendLayer($this->createLayer
('AgaviFileTemplateLayer', 'wrapper'), $this->getLayer('content'));
        $i->setTemplate('LiveSuccess.wrapper');

}

Cheers,

David

Am 29.01.2007 um 14:02 schrieb David Zülke:

_______________________________________________
users mailing list
us...@lists.agavi.org
http://lists.agavi.org/mailman/listinfo/users

    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Discussion subject changed to "Advanced Layout/Layers example (was: Re:IMPORTANT: Breaking changes in 0.11 branch: tons of new features!)" by Van Daele, Koen
Van Daele, Koen  
View profile  
(1 user)  More options Jan 30 2007, 11:18 am
From: "Van Daele, Koen " <koen.vanda...@rwo.vlaanderen.be>
Date: Tue, 30 Jan 2007 17:18:15 +0100
Local: Tues, Jan 30 2007 11:18 am
Subject: Re: [Agavi-Users] Advanced Layout/Layers example (was: Re:IMPORTANT: Breaking changes in 0.11 branch: tons of new features!)
Hi,

The new layout stuff works really well. I've just converted my app to using the layouts, took a bit of getting used to it, but it wasn't to difficult. And now I don't need to set up the decorator manually in the base view anymore. I've made the default view have a method like this:

public function executeHtml(AgaviRequestDataHolder $rd)
{
        $this->setupHtml($rd);

}

For simple views that just serve up a template without doing anyting else this means I don't even need to override the executeHtml() method anymore.

I have set up the setupHtml method so that it takes as an optional parameter the name of a layout, that way I can do setupHtml($rd, 'notDecorated') to have a view that renders without decoration (e.g. a view that needs to be inserted in some other slot) while the setupHtml($rd) automatically decorates with the standard site template. And I have a third layout that just wraps some simple layout around a template for pages like Unavailable.php.

The setting of slots for an inner layer is also very helpful. Something I've been waiting for a long time.

I haven't encountered any problems so far. One thing I do keep on running into is agavi telling me that a config file is not valid, but only giving back a very general error-message (Element Content is invalid) that doesn't really help in debugging. Maybe this is only a problem now because the config-file format has changed a few times. Is it possible to have more specific errors? It might also be useful to add the different parameters to xsd for validation since right now you don't get a warning if you've used a wrong parameter.

Anyway, thanks again for all the hard work,
Koen

...

read more »


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »

Create a group - Google Groups - Google Home - Terms of Service - Privacy Policy
©2009 Google