***Donate Here or in the panel to support the Gaia Framework project.***
Pages: [1]
  Print  
Author Topic: An Experiment with Gaia+PureMVC framework  (Read 2992 times)
Glidias
Gaia User
**
Posts: 10


« on: September 18, 2008, 02:01:16 AM »

Gaia versus PureMVC
--------------------

Face it. Gaia is a prescriptive framework, but a darn useful one at that. It has many useful and essential front-end features that can save lots of time.  No doubt, a lot of these frontend services could be done with PureMVC by coding in your own proxies, commands and mediators, and sometimes, these may come off neater since you might not end up using all the features in Gaia (actually, that's kind of unlikely too). With PureMVC, registering a single proxy here, a single proxy there, a mediator here or mediator there within your application, makes PureMVC, truly modular and robust.

Just that for most sites nowadays, we often need all the features found in Gaia already, and often in every page, and since it's already provided, why not leverage on them? All these essential features are built into every page, with not too much bloat. Great for novices and professionals that still don't have their own framework for development (or perhaps theirs lack a full feature-set of essential features). Sure, Gaia enforces a site.xml structure. That's how Steven Sacks work. But it's a good framework/strategy neverthelsss and also easy for beginners to get into.

Of course, you can't compare it to PureMVC's loosely coupled approach. Gaia wasn't made with that in mind because it is prescriptive to help automate various tasks. Yet, it's still flexible enough to work with other methods.

Gaia with PureMVC
------------------
Integrating with a backend framework, why not? As long as you've decided to work with (or do not really mind) Gaia's basic front-end file structure and site.xml, everything else is up to you. 

For non-PureMVC people, here's a quick general layman's rundown of PureMVC's framework:

1) A Command is a class which executes a certain action on the application framework.  It can send out notifications to the entire application.
2) A Proxy is a class that contains data-related/state information of the application. It can send out notifications to the entire application.
3) Mediators are classes which listen in on events dispatched by their assosiated view component(s) often due to interaction by the user. They handle events by executing a Command or sending out a notification to the rest of the application. They can also receiive notifications, and therefore tell their view components to update themselves (ie. handle the notification). Mediators can also get proxies to refer to any data-related information.
4) Your ApplicationFacade is your main application class which keeps tracks of your commands, proxies, mediators and notification types.


Gaia's Custom Initialisation:
Since Gaia has a init() method within GaiaMain, you can do whatever stuff prior to Gaia transitioning in it's first page. In this case, you can load in your PureMVC framework and other backend stuff like this:
Code:
override protected function init():void {
// Initialise SiteTrunk Singleton
SiteTrunk.getInstance();
// Run PureMVC
facade = MyApplicationFacade.getInstance();
facade.startup( { stage: this.stage,
   callback: initComplete }  );
}
This calls the applicatiton facade's startup method, and once it's done, I've also provided the callback function to let Gaia continue on seamlessly.

Minimalist implementation of PureMVC's management of mediators within Gaia Framework.
-----------------------------------------------------------------------------------------
Since Gaia's page structure already has built-in transition in/out methods, you can take advantage of this by de-registering/registering any needed mediators with your ApplicationFacade on the fly .
Code:
override public function transitionIn():void
{
super.transitionIn();
registerMediators();
}

override public function transitionOutComplete():void {
super.transitionOutComplete();
unregisterMediators();
}
And within your register/unregister mediator methods, specify what mediators to register/unregister. The names you can provide to the mediatorName can be the page's branch id, to keep the mediators unique to the page. It's entirely up to you here. Or you might register the view components with a currently existing mediator.

Main issue:

Good or Bad practice?
This pattern means Mediators are created when the page transitions in, since the view components (ie. the page and it's nested display-objects) is available by then. In some cases, you might prefer to have mediators beforehand rather than on the fly. This can be done in Gaia's custom initialisation. However, since src assets are loaded in dynamically via Gaia's prescribed style, you'll still have to register any view components manually with those pre-inited mediators upon transitioning in the pages.
If you're extremely strict about it, both occurances may seem to contradict MVC practice somewhat, since you're registering the mediator's view asset within the scope of the current view component (ie. your Gaia page), rather than on a purely application layer itself. However, given the way Gaia or Flash works (with src assets loaded in), this seems unavoidable, since your  code is already somewhat bundled into each page (heck, any DocumentClass in AS3 also does the same thing, it's unavoidable).  After all, mediators only seem more like mechanisms to allow your Gaia page to interact with the rest of the applications easily, and to do so in a loosely-coupled manner.  I guess it's unavoidable to be completely decoupled. Some "small bits" of application coding (ie. registering/unregisteirng mediators and proxies) has to be unavoidably done within the Gaia pages themselves. But I guess, if you keep the other intricate application code details outside the scope of these Gaia pages (with PureMVC's seperate mediators, commands and proxies, etc.), it should suffice neatly enough and meet PureMVC"s purpose without too much compromise. After all, the way i see PureMVC, it seems mediators, proxies and commands can be registered and executed whenever possible  (from anywhere) through the centrally available ApplicationFacade singleton. Just as coding in Gaia is done in multiple pages rather than being centralised to a single custom-coded controller page (since the Gaia framework already handles this), coding on the applicaiton layer can also be done in seperate files/modules with the help of mediators, proxies and commands under your ApplicationFacade.
« Last Edit: September 18, 2008, 11:25:13 PM by Glidias » Logged

My Flash Frameworks:
FakeMVC
SG-Camouflage
Glidias
Gaia User
**
Posts: 10


« Reply #1 on: September 18, 2008, 02:20:24 AM »

Gaia Templating...and Gaia Pages functioning as mediators themselves?
--------------------------------------------------------------------------
Gaia templating is the art of keeping as much view assets (ie. your visual components) outside the scope of your Gaia page class (ie. your AbstractPage). This often means using generic basicTemplate.swfs for your Gaia Pages which are linked to your BasicTemplate class. It's a pretty much empty shell template where your actual visual assets are loaded in externally via the Gaia page's asset nodes. The BasicTemplate handles positioning/transitioning/localization of these assets according to some liquid/container layout scheme on your application. This can come in useful when such visual assets are subjected to graphical changes/updates, but in some cases (key application interface elements) for example, putting these assets outside the scope of the Gaia page into an external swf is very inconvenient and completely makes no sense. In short, your page template need not necessarily be entirely an empty shell, just that it should make hierchal sense. Some visual components could still exist within the Gaia page itself especially if it's an integral part of the application on the top level.

Gaia templating is also a common practice in ensuring you don't have many unncessarily replicated files (eg. HomePage, AboutPage, DisclaimerPage, etc.) which often require recompiling again after making changes to other classes which your pages rely on. Using a generic template page swf and class to handle all this is much better, since positioning/transitioning/localization is automatically handled within the template page class. Your template can even load in actual class definitions for your loaded raw swf assets, rather than bundling the code into the swf itself. A templatePage can load and register a certain mediator to itself to allow it to interact with the rest of the application on a high level.

Eg. site.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>
<site title="Wildlife Galore: %PAGE%" menu="true" indexFirst="true" routing="false">
    <page id="index" src="index.swf">
<asset id="siteTrunk" src="sitetrunk.xml" />
<page id="shell" src="templateShellPage.swf" depth="top">
<page id="welcome" title="Welcome" src="templateLandingPage.swf">
<page id="wildlife_galore" title="WildLife Galore" src="templatePage.swf">
</page>
<page id="about" title="About" src="templatePage.swf" />
<page id="wonders" title="Wonders" src="templatePage.swf" />
<page id="wildlife" title="WildLife" src="templatePage.swf" />
<page id="vegetation" title="Vegetation" src="templatePage.swf" />
<page id="articles" title="Articles" src="templatePage.swf" />
<page id="eco" title="Eco-Volunteers" src="templatePage.swf" />
        </page>
    </page>
</site>

Eg. sitetrunk.xml. Assets are transfered from sitetrunk.xml into site.xml automatically for preloading or on-demand-loading into each page branch. Each branch also contains whatever asset layout/container/localization data which is easily retrievable from BasicPageTemplate's methods. With SiteTrunk, it allows you to easily retrieve any other information specific for your current project.
Code:
<?xml version="1.0" encoding="UTF-8"?>
<trunk>

<branch id="default">
<links>
<link id="layoutBranch">index/shell</link>
</links>
</branch>

<branch id="index/shell">
<links>
<link id="layoutParent">layouter</link>
<link id="divider">|</link>
<link id="navItem1" href="#shell/about"><![CDATA[ About ]]></link>
<link id="navItem2" href="#shell/wonders"><![CDATA[ Wonders ]]></link>
<link id="navItem3" href="#shell/wildlife"><![CDATA[ WildLife ]]></link>
<link id="navItem4" href="#shell/vegetation"><![CDATA[ Vegetation ]]></link>
<link id="navItem5" href="#shell/articles"><![CDATA[ Articles ]]></link>
<link id="navItem6" href="#shell/eco"><![CDATA[ Eco-Volunteers ]]></link>
<link id="splash" href="splash"></link>
<link id="home" href="shell/welcome"></link>
<link id="disclaimer" href="shell/disclaimer">Disclaimer</link>
</links>
<assets>
<asset id="blogFeed" src="blog.swf" layoutId="right" />
</assets>
<layouts>
<layout id="stager" liquid="stage">stager</layout>
<layout id="disclaimer" liquid="bottom">disclaimer</layout>
<layout id="left" liquid="leftRef">left</layout>
<layout id="top" liquid="topRef">top</layout>
<layout id="right" liquid="right">right</layout>
<layout id="middle" liquid="middle">middle</layout>
</layouts>
<containers>
<container id="mainWindow">windowPane.myWindow</container>
</containers>
<text>
<copy id="disclaimerText"><![CDATA[DISCLAIMER: Lorem Ipsum]]></copy>
<copy id="mainPanel"><![CDATA[About  |  Wonders  |  WildLife  |  Vegetation  |  Articles |  Volunteers]]></copy>
<copy id="landingBtn" type="arrayOfStrings"><![CDATA[ ]]></copy>
<copy id="infoPager"><![CDATA[ ]]></copy>
<copy id="siteTitle">WildLife Galore</copy>
</text>
</branch>


<branch id="index/shell/welcome">
<assets>
<asset id="mainContent" src="welcome.swf" layoutId="middle" containerId="mainWindow"localize="true" />
<asset id="blogFeed" src="about.swf" layoutId="middle" containerId="mainWindow"/>
    </assets>
<layouts>
<layout id="pageArea" liquid="top">pageAreaZone</layout>
</layouts>
<containers>
<container id="pageWindow" liquid="top">pageAreaWindow</layout>
</containers>
<text>
<copy id="infoPager">Welcome to the Site!</copy>
<copy id="landingBtn" type="arrayOfStrings">View related links |||| Upcoming Presentations</copy>
</text>
</branch>


<branch id="index/shell/welcome/wildlife_galore">
<links>
<link id="layoutBranch">index/shell/welcome</link>
</links>
<assets>
<asset id="mainContent" src="wildlife_galore.swf" layoutId="middle" containerId="mainWindow"/>
<asset id="page1" src="wildlife_galore_1.swf" layoutId="pageArea" containerId="pageWindow"><![CDATA[ Page 1 copy Lorem Ipsum.... ]]></asset>
<asset id="page2" src="wildlife_galore_2.swf" layoutId="pageArea" containerId="pageWindow"><![CDATA[ Page 2 copy Lorem Ipsum.... ]]></asset>
<asset id="page3" src="wildlife_galore_3.swf" layoutId="pageArea" containerId="pageWindow"><![CDATA[ Page 3 copy Lorem Ipsum.... ]]></asset>
<asset id="page4" src="wildlife_galore_4.swf" layoutId="pageArea" containerId="pageWindow"<![CDATA[ Page 4 copy Lorem Ipsum.... ]]></asset>
    </assets>
<text>
<copy id="infoPager">WildLife Galore</copy>
<copy id="landingBtn" type="arrayOfStrings">View related links |||| Profile Explorations</copy>
<copy id="page1CatchLine"><![CDATA[ 1 Lorem Ipsum ]]></copy>
<copy id="page2CatchLine"><![CDATA[ 2 Lorem Ipsum ]]></copy>
<copy id="page3CatchLine"><![CDATA[ 3 Lorem Ipsum ]]></copy>
<copy id="page4CatchLine"><![CDATA[ 4 Lorem Ipsum ]]></copy>
</text>
</branch>

A not-so-good practice:
If it happens that your Gaia page template has absolutely no visual components within the scope of the class (or you're just too lazy to manually register specific mediators for specific visual components), you might want to consider having the Gaia pages function as "high-level mediators" themselves. Of course, this is really a "dirty" tactic, but sometimes, I may want all my pages to be able to conveniently receieve notifications and update the view components/assets directly within the same class, without having to explicitly declare another mediator to handle in another page). Take note though, that Mediators in PureMVC shoudld't do too much. Thus, by letting my Gaia pages function as Mediators, I'm actually breaking the rules here and therefore this "PageMediator" isn't strictly a mediator alone, it's doing a lot more than it should. Also, there' no stopping you from creating other sub-mediators within the "PageMediator". Use the following example at your own risk. The page's mediator's mediatorName is set to the page's branch id by default. You'd also lack the funcitonality of a mediator being able to switch view components, since it's part of the page itself.
Eg.
Code:
/*****************************************************************************************************
* GaiaPureMVC Helper Framework for Adobe Flash ©2007-2008
* Written by: Glenn Ko
* An experimental fusion between PureMVC and Gaia.
* -- PureMVC --
*  PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved.
*  Your reuse is governed by the Creative Commons Attribution 3.0 United States License
* -- Gaia --
* Written by: Steven Sacks
* blog: http://www.stevensacks.net/
* forum: http://www.gaiaflashframework.com/forum/
* wiki: http://www.gaiaflashframework.com/wiki/
*
* By using the Gaia Framework, you agree to keep the above contact information in the source code.
*
* Gaia Framework for Adobe Flash is (c) 2007-2008 Steven Sacks and is released under the GPL License:
* http://www.opensource.org/licenses/gpl-2.0.php
*****************************************************************************************************/

package org.gaiapuremvc.as3.templates
{
import com.gaiaframework.templates.AbstractPage;
    import org.gaiapuremvc.as3.view.DelegateMediator;
import org.puremvc.as3.interfaces.IMediator;
import org.puremvc.as3.interfaces.IFacade;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.interfaces.INotifier;
import org.puremvc.as3.patterns.facade.Facade;

/**
* If you want your Gaia pages to function directly as a mediator itself in your PureMVC framework (allowing your
* page to receieve and handle notifications as well), get your page to extend this class instead.
* It extends Gaia's typical AbstractPage template but also implements IMediator, INotifer.
*
* Upon transitioning in, the page registers itself as a mediator and de-registers upon transitioning out.
* You can prematurely un-register the page  by using the method unregisterMediator() manually in your
* extended class. Note that registerMediator() only works properly when the page transitions in.
*
*/
public class AbstractMediatorPage extends AbstractPage implements IMediator, INotifier
{
private var mediatorName:String="";
protected var facade:IFacade = Facade.getInstance();

/**
* Constructor.
*/
public function AbstractMediatorPage()
{
super();
}

override public function transitionIn():void
{
super.transitionIn();
registerMediator();
}

override public function transitionOutComplete():void {
super.transitionOutComplete();
unregisterMediator();
}


/**
* Manually registers the page mediator to the facade.
*/
protected function registerMediator() {
mediatorName = this.page.branch;
if ( !facade.hasMediator( mediatorName ) ) {
facade.registerMediator( this );
}
}

/**
* Manually unregister the page mediator from the facade.
*/
protected function unregisterMediator() {
if ( facade.hasMediator( mediatorName ) ) {
facade.removeMediator( mediatorName );
}
}


// -- IMediator implementations

// -- OVERWRITE your own implementation in the extended class.
public function listNotificationInterests():Array { return []; }
public function handleNotification( notification:INotification ):void {}
public function onRegister():void {}
public function onRemove():void {}



public function sendNotification( notificationName:String, body:Object=null, type:String=null ):void
{
facade.sendNotification( notificationName, body, type );
}

public function setViewComponent( viewComponent:Object ):void
{
// -- Shouldn't allow the user to change the view component since it's fixed to this class.
// Do nothing
}

public function getViewComponent():Object
{
return this as Object;
}

public function getMediatorName():String
{
return mediatorName;
}


}
}

Best practice argument:
Generally, i do feel sometimes it's traditionally better to define your mediators seperately from the view. After all, not all Gaia pages might need a mediator, and defining a unique mediator in a seperate file allows other developers to know that there's something special about a particular component in relation to the rest of the application. It keeps high level and low-level application code seperate, which is a good thing.  Besides, the traditional way of registering mediators manually also means Gaia templating isn't enforced all the time, and can still have another unique AboutPage that can function as the viewComponent under a generic PageMediator. Why lump the 2 together and restrict yourself? Depending on your application, you can also have 1 mediator handling multiple pages, rather than being restricted to one mediator per page.

Yet, in some cases, there may be nothing seriously wrong with a prescribed mediator template extending from AbstractMediatorPage that already has some integrated built-in visual components, loosely coupled together with various other external assets. You'd also have event hijacking functionality bundled in as well under Gaia. Sure, this doesn't *totally* seperate view from logic, but is that necessary all the time? How often do we end up re-using mediators seperate from the view? Yet, if you do it often (or intend to), especially in other future applications, than adopt the best practice, but this often means restricting many of Gaia's functionality to the front-end aspects only. In some cases, combining both functionalities from both frameworks into a single page can save a lot of time, even though it breaks PureMVC's best practice. In general MVC, this isn't much of an issue because both GaiaPage and Mediator can be treated as part of a general View context and any other high end logic can be delegated elsewhere through commands and proxies, but PureMVC likes to take this one step further by enforcing the use of "pure mediators" which shoudn't handle visual changes in anyway. Yet more often than not, I see many situations as well where both application-specific mediator and it's visual component is tightly coupled (1 to 1 exclusive relationship), so tying them together doesn't result in any serious problems.
« Last Edit: March 09, 2009, 10:04:59 AM by steven » Logged

My Flash Frameworks:
FakeMVC
SG-Camouflage
Glidias
Gaia User
**
Posts: 10


« Reply #2 on: September 18, 2008, 04:32:24 AM »

The thing is, when combining 2 frameworks, you've got a lot of options (and power) to go with it and it can get real messy. With great power, comes great responsibility.

For example, Gaia's features like event hijacking doesn't necessarily make integration with PureMVC better. It just provides more options. For eg. You can conveniently let the Gaia's command run first, than run the event hijack which also dispatches an event to the mediator, and wait for a callback notification from "somewhere". Or, you can run PureMVC's command first, and if confirmed, run Gaia's command and let Gaia continue onwards from there, keeping Gaia restricted purely on the front-end view.

The conflict comes because Mrs. Gaia integrates both view and logic together. Mr. PureMVC doesn't.

When it comes to event hijacking under Gaia (which is often a shortcut to easily add on-the-fly functionality to your application), we know that traditional pureMVC mediators can't receive Gaia hijacks directly and default Gaia pages that receive hijacks can't execute Commands directly but through a mediator (unless you use my prescibed hackish Gaia+PureMVC AbstractMediatorPage, which combines functionalities from both frameworks). So, if you want to keep both frameworks loosely coupled, you'd have to dispatch another event during hijacks for pureMVC's mediator to capture it and execute the command, which can be somewhat inconvenient. Thus, i've resolved to conclude that it's often best that such event hijacks under Gaia should not handle application logic at all in traditional PureMVC context.  PureMVC commands should run first, than notify all listening Gaia page mediators to respond visually. So far, AbstractMediatorPage  is quite untested, though I did find it very convenient, yet, i am also aware it's like a compromise to achieve more functionality and speed, since it isn't Mr. PureMVC's practice. Yet,  Mrs. Gaia finds it okay to combine a bit of view with logic.

So, for Purist PureMVC users that wish to treat Gaia purely on the front-end, than you'd still have to code in various commands before notifying (probably the IndexMediator for IndexPage) to redirect the page. Also, if you decide to let Mrs. Gaia handle some application logic as well (since Gaia often has a more "powerful toolset" and "shouldn't Gaia.api.goto be treated as commands as well?"), that might rub on Mr. PureMVC's feathers and his definition of a Command (which is:"Do something on the application layer and send notification").  Mr. PureMVC can make you traditionally code much better if you keep Mrs. Gaia page's code restricted to the front-end only, which means your event hijacks should only handle visual changes on the page itself, and at the last resort dispatch any events to her mediator when further application logic is required. Often, for applications where the Commands are quite specific and require integration with the backend prior to running a front-end operation, PureMVC's approach is rock-solid and may not depend on any hijack to trigger anything (so far, my hijacks mainly handle visual changes purely within Gaia's visual frontend). So, the best practice would be: Run PureMVC's Command first, than notify a GaiaMediator to execute one of Gaia's powerful toolset of other front-end commands, and everything should be fine. Actually, GaiaMediator could also be seen as a GaiaProxy, since both can run stuff. Or you could just send a specific notification to the IndexMediator to handle it Gaia-style. Or you could also embed a Gaia.api command within a PureMVC Command, but this will tightly couple the 2 frameworks rather than synchronising the 2 frameworks. Gasp.. the possibilities...lots of possibilities. By combining 2 frameworks, there's a lot of power here. Whatever way you choose to synchronise the actions between the 2 frameworks, please know what you're doing. You definitely have far more layers to make use of now.

Sigh, sounds like an uneasy marriage in real life. But no doubt, combining both frameworks is very powerful indeed. Just be aware of the roles and know that CLEARLY  seperating the roles between the 2 frameworks will make up for far neater application code on the long run.

p.s. If you choose to take a risk with the AbstractMediatorPage approach, by all means.
« Last Edit: September 18, 2008, 11:54:08 PM by Glidias » Logged

My Flash Frameworks:
FakeMVC
SG-Camouflage
jsus2maj7
Gaia Expert
****
Posts: 316

2725b7b1f6b


« Reply #3 on: October 16, 2008, 06:59:19 AM »

interesting posts
why kind of overhead are we talking here?

i've always thought that there is room for Gaia to be more modular and extensible
maybe something down this route?
Logged
steven
Gaia Author
Administrator
Gaia Evangelist
*****
Posts: 2687


gaiaframework
WWW
« Reply #4 on: October 17, 2008, 10:15:12 AM »

You should unregister before you call transitionOutComplete.
Logged

Steven Sacks | Gaia Author
Please support Gaia by donating to the project!  Smiley
Ramiro
Gaia Intermediate
***
Posts: 88



« Reply #5 on: December 13, 2008, 08:19:36 AM »

Hi Glidias
I would love to hear more about this, if you've researched more and can spare the time  Kiss

Hug!
Logged
doofmoof
Gaia Novice
*
Posts: 2


« Reply #6 on: January 29, 2009, 03:25:56 PM »

Hey, Thanks very much for all this, it's an awsome topic that i realy need to explore right now.  Been using gaia for a while and am now learning PureMVC, trying to work out how to integrate them.  It made my day to find this post which at least shows there is some logic to the idea of using Gaia and PureMVC together ( and i suppose many other frameworks out there)

After reading through this post i think i have found an answer to some of the potential difficulties of using PureMVC over seperate Gaia pages...
It is the newer multicore version of PureMVC ( http://trac.puremvc.org/PureMVC_AS3_MultiCore ).  It seems it's all about using PureMVC with modular applications (like Gaia?), where the core PureMVC players are implemented as "Multiton Core actors instead of the Singletons used in the Standard Version".  So I guess that means each gaia page would have it's own set of MVC core and facade which could communicate with each other using this pipes utility (http://trac.puremvc.org/Utility_AS3_MultiCore_Pipes). 

Like i say I'm just beggining to learn PureMVC, and I'm definatly going to look very deeply into this now and I will report back here with anything I manage to come up with, however there are people out there who are much brighter than me on this, who I'm sure could pull something together much better and quicker than me.  I think a solid basic tutorial  to cover integrating this would help a lot of people out (including me) and get us realy pushing Gaia.
Logged
jsus2maj7
Gaia Expert
****
Posts: 316

2725b7b1f6b


« Reply #7 on: January 29, 2009, 05:04:51 PM »

I've been looking at this as well.

it seems if you're going to merge the two,
it's really going to be pure mvc with a heavily modified gaia on top

i don't believe the different versions of puremvc would make a real difference
it would just set how modular your framework would be and how much overhead there is

i think it would be best not to think
"how do i merge puremvc and gaia?"
but
''if i was going to build a framework on top of puremvc how would i do it?"

i think the latter would result in something very gaia-esque but more flexible
albeit, at the cost of a bigger footprint (10-15k guess)

also, it should be made clear that this is not a knock on gaia in any manner
it's just that a more mvc approach has advantages in a lot of respects
disadvantages too but these imho are worth it
Logged
Pages: [1]
  Print  
 
Jump to:  

TinyPortal v1.0 beta 4 © Bloc