Wednesday, May 23, 2007

Context and Multiple Dispatching

PWB has a mechanism for multiple dispatching.

Some background:

In an Object Oriented language, when you send a message to an object, which method is executed is defined by the class of the receiver.

class ObjectA{function a(){echo 'a';}}
class ObjectB{function a(){echo 'b';}}

$a = new ObjectA();
$a->a();//echoes 'a';

$b = new ObjectB();
$b->a(); //echoes 'b';

As you can see, same message + different class -> different method. This is single dispatching.

PWB provides a way to use Multiple Dispatching. That is, to choose the method based on the types of the parameters to the function. It provides the macro defmdf ("define multiple dispatch function"), and the function mdcall.

#@defmdf assign($user:User, $area:Area){
$user->addArea($area);
}@#

#@defmdf assign($user:User, $group:Group){
$user->addGroup($group);
}@#

#@defmdf assign($file:File, $group:Group){
$file->addGroup($group);$group->attachFile($file);
}@#

And then, to use them:
mdcall('assign', array($user, $group));

Context dispatching, is a similar concept, but based on the types of the context instead of the type of the parameters:

#@defmdf showNews [NewsList] ($news:News){
echo $news->title; echo $news->author->name;
}@#

#@defmdf showNews [NewsView] ($news:News){
echo $news->title; echo $news->title; echo $news->body;
}@#

#@defmdf showNews [AuthorView<-NewsList] ($news:News){
echo $news->title;
}@#

In this case, we show news.
Showing a news article, when you are showing a list of news, is just showing the title, and the author.
Showing a news article, when you are showing only a news article, is showing the title, the author, and the body.
Showing a news article, when you are showing a list of news, in the context of "showing an author", is just showing the title (we already know the author's name from the context).

So, we would call the function:

mdcompcall('showNews',array($context, $news));

the first parameter to showNews (the first element in the array) is the context to use.

This techniques are tholoughly used in YATTAA!

YATTAA!

YATTAA is a new part of PWB. It is basically, a set of classes for building CRUD-based applications, but aiming at making complex applications (having CRUD related tasks simplified).

- CRUD with Create, Update and Delete based on PWB's transparent persistence.
- Object Lists, with automatically generated filters and orders (but redefinable).
- Modules based application, with Landmark pattern for accesing modules, and ActiveReference for accesing module's navigation history.
- Navigation and Operation menus for Components.

Actually, we built a complete and very interesting contact list, creating the class Contact, and using as root of the ContactListModule:

new ObjectsAdmin(#@select Contact@#);

YATTAA stands for Yet Another Toolkit for Template Application Assembly (Obiously, a backronym, based on Heroes' character Hiro).

Sunday, April 22, 2007

PWB's Permission Checking

While discussing permissions, this example came to mind:

You have a discussion group. You want users to post messages, and those messages, you want them distributed by mail to all of the subscribers.

But, at the same time, you don't want that any of the users be able to list all of the subscribers' mails.

This simple example showed us that simple read-write permissions on database objects wouldn't do. We needed to allow or deny users different "actions", but those actions could use as many different objects of the database as needed.

Other interesting thing we needed, was the ability to use many variables to check if someone was able or not to perform an action. For example, you could want someone to be able to post a message during the morning, but not in the afternoon (I don't really know why, but I needed an example using time variables). Or only being able to delete or modify a post if it's the administrator, or the author.

So, what was needed here was extensive programming.

The result of all of this:
  • Permissions are created for components. To check if someone is able to see a component, the component's #checkAddingPermission method is called. If it returns true, it can be added. The component is already created (but not initialized), so checking the creation parameters is possible.

class UserAdmin extends Component{
function checkAddingPermission(){
return User::logged()->isAdmin();
}
...
}
  • CommandLinks have a special #checkAddingPermission: they check their FunctionObject's permissions. And them, check the 'check'.methodname.'Permission' method.
class OtherComponent extends Component{
function initialize(){
$this->addComponent(new CommandLink('proceedFunction'=>new FunctionObject($this, 'doSomething')));
}
function doSomething(){...}
function checkDoSomethingPermission(){return its_the_morning();}
}

Summing up:
  • Only actions have permissions.
  • Permissions can be implemented however you want them to (even adding read-write permissions in the database ;)).
  • A component disabled by permissions, won't be in the application's components tree, so can be seen, nor activated.

Labels:

Saturday, February 10, 2007

PHPCC's Grammar

Here's the grammar for parsing grammars:

<grammar(
alternative::={["[a-zA-Z_][a-zA-Z_0-9]*","=>"],<sequence>;"\|"}.
maybe::="\[",<alternative>,"\]".
list::="\{",<alternative>,";",<alternative>,"\}".
sequence::={["[a-zA-Z_][a-zA-Z_0-9]*","->"],(<list>|<maybe>|<symbol>|<subparser>|alt=>"\(",<alternative>,"\)");","}.
subparser::="<",name->"[a-zA-Z_][a-zA-Z_0-9]*",">".
symbol::=""[^"]+"".
non-terminal::="[a-zA-Z_][a-zA-Z_0-9]*","::=",<alternative>,"\.".
grammar::="\<","[a-zA-Z_][a-zA-Z_0-9]*","\(",(<non-terminal>)*,"\)","\>".
)>

Labels:

Friday, February 09, 2007

OQL Queries

We finally have queries for the objects in the database. They're used with the macro #@select.

An example:
$r =& #@select RolePermission (permission as permission) from p: RolePermission, u:UserRole where p.role =u.role AND u.user=$id@#;
And the complete grammar:
'<oql( condition="subexpression=">"\(",<expression>,"\)"|comparison=><value>,"=|<=|>=|LIKE",<value>.
expression::=logical=><condition>,operator->"AND|OR|and|or",<condition>|condition=><condition>.
oql::=class->["[a-zA-Z_][a-zA-Z_0-9]*"],
fields->["\(",fields->{"[a-zA-Z_][a-zA-Z_0-9]*","as","[a-zA-Z_][a-zA-Z_0-9]*";","},"\)"],
from->["from",from->{var->"[a-zA-Z_][a-zA-Z_0-9]*",":",class->"[a-zA-Z_][a-zA-Z_0-9]*";","}],
where->["where",expression-><expression>].
variable::={"[a-zA-Z_][a-zA-Z_0-9]*";"\."}.
value::=var=><variable>|
value=>(number=>"[0-9]+"|
str=>"\'[^\']\'"|
phpvar=>"\$[a-zA-Z_][a-zA-Z_0-9]*"|
bool=>"TRUE|FALSE|True|False|true|false").
)>'

Labels: ,

PHPCC

Now we have a Compiler Compiler (the same idea as yacc and similar): PHPCC. You define a grammar, and it returns an object that can parse a tree (array of arrays) for you.
To use it, you define a grammar, use the PHPCC::createGrammar method, add necesary pointcuts in the parsed tree, and then parse the string.
A necessary example:
$oqlg =&PHPCC::createGrammar('<oql( condition="subexpression=">"\(",<expression>,"\)"|comparison=><value>,"=|<=|>=|LIKE",<value>.
expression::=logical=><condition>,operator->"AND|OR|and|or",<condition>|condition=><condition>.
oql::=class->["[a-zA-Z_][a-zA-Z_0-9]*"],
fields->["\(",fields->{"[a-zA-Z_][a-zA-Z_0-9]*","as","[a-zA-Z_][a-zA-Z_0-9]*";","},"\)"],
from->["from",from->{var->"[a-zA-Z_][a-zA-Z_0-9]*",":",class->"[a-zA-Z_][a-zA-Z_0-9]*";","}],
where->["where",expression-><expression>].
variable::={"[a-zA-Z_][a-zA-Z_0-9]*";"\."}.
value::=var=><variable>|value=>(number=>"[0-9]+"|str=>"\'[^\']\'"|phpvar=>"\$[a-zA-Z_][a-zA-Z_0-9]*"|bool=>"TRUE|FALSE|True|False|true|false").
)>'
);
$oqlg->addPointCuts(array (
'condition' => new FObject($this, 'parseCondition'),
'expression' => new FObject($this, 'parseExpression'),
'oql' => new FObject($this, 'parseOql'),
'variable' => new FObject($this, 'parseVariable'),
'value' => new FObject($this, 'parseValue'),
));
$config =& $oqlg->compile($query);
We first define the grammar of a query to our objects. Then, we add the pointcuts (function objects that are called when, for example, a 'value' is finished parsing) to transform the parsed tree to usable data. And then, just ->compile() the string.

The PointCuts are separately, so anyone can use a well defined parser to compile different results. The grammar and the semantics of the same language are separate.

Labels:

Tuesday, February 06, 2007

Window>>setTitle()

Something that was overdue: You can now set the window title dynamically.

$w =& Window::getActiveInstance();
$w->setTitle('the window title');

Labels:

Wednesday, January 31, 2007

New Website

As the previous website (a mediawiki hosted in sourceforge) was badly configured, it became loaded of spam links. So we now switched to the wiki the new google groups provide.

And we'll be extending the documentation soon.

Labels:

COMET

A new Page renderer has been developed. While it doesn't provide new functionalities (as it would be very useful if PWB had any collaborative tools, but it doesn't), it provides better performance, as the application (code and session data) only has to be loaded once by the user.

It's a standard comet implementation: An iframe is kept inside the page, loading an infinite stream of data, and the application is loaded and kept alive in this iframe. Requests are made through the XMLHttpRequest js object.

It still needs to be tested in other browsers than Firefox, and windowing support is limited, but it's in the way. You can try it restarting the application while sending render=comet in the url.

Labels:

Windows

An Application can use different windows, which can communicate with each other. They are also updated when are changed from another window.

Usage:

$w =& new Window($aComponent, $windowName);
$w->open(); or $w->open("width=500px");

Labels:

Thursday, December 28, 2006

tagName in containers

Due to problems with Internet Explorer, containers now have an attribute "tagname", that is used when it's time to render it.

For example, when you have an elements list (ul), and this kind of template:

<ul id="list">
<container class="ListElement">
</ul>

after adding 2 List Elements, the rendered HTMl will look like

<ul id="list">
<li id="list:1">1st Element</li>
<li id="list:2">2nd Element</li>
<div style="visibliity:hidden"/>
</ul>

The div element will not render correctly in IE, causing AJAX to stop working. Here's where you need to use the "tagname" attribute:

<ul id="list">
<container class="ListElement" tagname="li">
</ul>

creating this HTML

<ul id="list">
<li id="list:1">1st Element</li>
<li id="list:2">2nd Element</li>
<li style="visibliity:hidden"/>
</ul>

Which renders correctly.

Templates just use the tagname of their first element.

Labels: