Tuesday, July 29, 2008

Fat Models

no not those models :P CakePHP models :)

If you find your self doing something like this in the controller:

$this->A_Model->query("update somedatabase set idstatus = '".$idstatus."', statuslastmodified = '".time()."' where idproject = '".$idproject."' and UID= '".$UID."'");


consider putting this type of logic as a function in the model and then calling the function.

Better yet try not to use custom queries if you can and use the model's built in functions.

Tuesday, July 22, 2008

Flex MXML binding The entity name must immediately follow the '&' in the entity reference

So your trying to do some mxml binding with conditional logic like:


<mx:Button label="Sign In" enabled="{emailTextInput.text != '' && passwordTextInput.text != '' }" />


when you compile you get this error, "The entity name must immediately follow the '&' in the entity reference"

THe parser looks at the & and expects an entity reference because the &
signifies the start of an entity reference within XML. So how do we tell it that it's actually a condition-AND operator?

Replace each of the & '& a m p ;' <-- no spaces


Compile, and viola it works!

Wednesday, July 16, 2008

Flex: Is it a memory leak?

tutorial how flash garbage collection works:

http://blogs.adobe.com/aharui/GarbageCollection/GCAtomic.ppt



heres a couple good quotes from the presentation:

-The collector is not guaranteed to find all collectible blocks in one pass
Don’t want to interfere with rendering and interaction, Thus memory may never return to the initial point

-You are generally only concerned about repeatable sequences: Popup dialogs coming and going Modules loading and unloading. If you repeat these sequences often over a long period of time, the amount of totalMemory will hit some maximum value and stay at or below it if you are not leaking memory.

-Garbage Collection is not predictable



good tutorial on using the flex profiler:

http://www.peachpit.com/articles/article.aspx?p=1182473&seqNum=2




Tuesday, July 15, 2008

Flex Actionscript 3.0 Missing DisplayObject Children

So you have a component like


Awesome.mxml

<mx:Canvas ....>
<mx:Vbox id="avbox" />
</mx:Canvas>



if you try to create your object with as3 like:



var awesome:Awesome = new Awesome();



you'll see that



trace(awesome.avbox); //NULL



gives you null. Thats because your component's children are created using deferred instantiation. If you want to force the component to create its children do:


awesome.initialize();


now when you try:


trace(awesome.avbox); //NOT NULL


there will actually be something there :)

Actionscript 3.0 Flex Modules Tricky Timer Memory Leak

Becareful when unloading modules to stop and remove Timers, these can cause you memory leaks (and headaches)!!

Friday, July 11, 2008

PureMVC Pipes disconnecting the pipes after a module is unloaded TeeSplit

because TeeSplit can't remove a pipefitting of our choosing I extended it
and added in this method:


public function removePipeFitting(inpipe:IPipeFitting):IPipeFitting {
var outputsac:ArrayCollection = new ArrayCollection(outputs);

if (outputsac.getItemIndex(inpipe) >= 0) {
return outputsac.removeItemAt(outputsac.getItemIndex(inpipe)) as IPipeFitting;
}
return null;

}




in a PipeAwareModule you can add a reference to the pipes connecting to it.



private var _cachedPipes:ArrayCollection = new ArrayCollection([]);


/**
* used to remove pipes at the shell's side of the junction
*
* @param pipefitting we are going to call disconnect on this pipefitting
* @param output
*
*/
public function cacheFitting(pipefitting:IPipeFitting, output:IPipeFitting):void {
_cachedPipes.addItem({pipefitting:pipefitting, output:output});
}

/**
* goes through the arraycollection disconnecting the pipefitting from output
*
*/
public function removepipes():void {
for each (var pipefittings:Object in _cachedPipes) {
if (pipefittings.pipefitting is TeeSplitImproved) {
(pipefittings.pipefitting as TeeSplitImproved).removePipeFitting(pipefittings.output);
}
else {
(pipefittings.pipefitting as IPipeFitting).disconnect();
}
}
_cachedPipes.removeAll();
}


if your module has a unload method you'd want to:

1] call facade.removeMediator on every mediator (in the junction mediator's onRemove method do junction.removePipe on all pipefittings attached to that junction), facade.removeProxy, ..etc
2] call the removepipes() to remove incoming pipes, then
3] call facade.removeCore to get rid of all the core actors.

PureMVC Pipes Connecting / Disconnecting Pipes TeeMerge


===pipe1========= teemerge ---------------
|
======pipe2==========|


To get something like above you could either do


pipe1.connect(teemerge);
pipe2.connect(teemerge);


OR equivalently


teemerge.connectInput(pipe1);
teemerge.connectInput(pipe2);




disconnecting pipes

lets say we had:

======pipe1========pipe2==========pipe3=========

doing:
pipe1.disconnect().disconnect().disconnect();


would result in:


=====pipe1 pipe pipe3

because each disconnect returns the disconnected pipe






===teesplit==============pipe1=====
|
|==========pipe2=====
|
|==========pipe3=====


in this case

teesplit.disconnect();
teesplit.disconnect();
teesplit.disconnect();

removes all 3 pipes in the reverse order that you connected them.


there is no built in capability to remove a pipe of your choosing at the moment(something you need if your gonna be doing a lot of module building!!).

check here to see how someone implemented it themself:
http://forums.puremvc.org/index.php?action=printpage;topic=508.0


Thursday, July 10, 2008

PureMVC Pipes - using the filter

The filter is useful for filtering the messages going through the pipes. An analogy you can use is a water filter.


wall ======== [water filter] ========= fridge/sink
pipe1 pipe2



in code form this would look like this:


// create output pipes 1
var pipe1:IPipeFitting = new Pipe();
var pipe2:IPipeFitting = new Pipe();

// create filter
var filter:Filter = new Filter( 'TestFilter' );

// connect input fitting
var connectedInput:Boolean = pipe1.connect( filter );

// connect output fitting
var connectedOutput:Boolean = filter.connect( pipe2 );




the way something gets filtered is this..

1] the message going through the filter must have a variable in the message header,
lets call it particleSize:int.

example:


var h20Message:IPipeMessage = new Message( Message.NORMAL, { particleSize:1, name:'h20'} );
var mineralMessage:IPipeMessage = new Message( Message.NORMAL, { particleSize:10, name:'mineral'} );


2] the filter also needs a paramater to compare wether to let the message through or not, lets call it maxParticleSize.

3] then the filter needs a filter function which checks if the maxParticleSize >= particleSize.


How to setup the filter

to make it do something we have to create the filter and pass in parameters to the constructor.


/**
* Constructor.
*


* Optionally connect the output and set the parameters.


*/
public function Filter( name:String, output:IPipeFitting=null, filter:Function=null, params:Object=null )
{
super( output );
this.name = name;
if ( filter != null ) setFilter( filter );
if ( params != null ) setParams( params );
}


One way to create the filter is to pass in all name, output, filter and params to the functions parameters:


var teeMerge:TeeMerge = new TeeMerge();
var filter:Filter = new Filter( 'waterFilter',
new PipeListener(this,function ( message:IPipeMessage ){trace('particle got through');),
function( message:IPipeMessage, params:Object ):void { if (message.getHeader().particleSize >= params.maxParticleSize) throw new Error('particle filtered'); },
{ maxParticleSize:5 }
);
teeMerge.connect(filter);
junction.registerPipe( PipeAwareModule.STDIN, Junction.INPUT, teeMerge );


above, messages enter input the teeMerge, then goes to the filter, if the filter
accepts the message, it sends it to the pipelistener.



If you look at the Filter constructor you can see output, filter and params are optional and can be set/modified after creation of the filter which I will explain how that works below.

setting the output
filter.connect(a IPipeFitting)

setting the log level/filter function
If you look at Filter's source, its write method is where the filter changes it's log level and filter function. When the filter sees a FilterControlMessage.SET_PARAMS it will then try to set the new params.

below I set the maxParticleSize to 10 because i want the mineral to go through:


var maxParticleSize:number = 10;
var setLogLevelMessage:LogFilterMessage = new LogFilterMessage(FilterControlMessage.SET_PARAMS, particleSize);
filter.write(setLogLevelMessage );


of course, you can send the FilterControlMessage through a modules junction to the
filter using junction.sendMessage as well.

Wednesday, July 9, 2008

Adding a Resize button to a Flex Panel v2

check out this guys resizable panel: http://www.aboutflex.net/flex/resizable-panel/

Its pretty cool, but it won't work if you try to add the panel inside another component. Thats because handleScale directly references the stage to get the mouse coordinates. But if your component does not start at x=0 y=0 then the cordinates will be off.

here is the modified version which uses the parents mouseX,mouseY in handleScale:






heres the source:

ResizablePanelv2.mxml


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:containers="com.flexas3.containers.*">
<mx:VBox horizontalAlign="center" verticalAlign="middle" backgroundColor="#696969" horizontalCenter="0" verticalCenter="0" width="50%" height="75%">
<mx:Button label="I'm in a Vbox" />
<containers:ResizablePanel title="Resize Me !!" x="73" y="110" width="258" height="213" />
</mx:VBox>
</mx:Application>



./com/flexas3/containers/ResizablePanel.as

package com.flexas3.containers
{
import flash.events.MouseEvent;

import mx.containers.Panel;
import mx.controls.Button;

public class ResizablePanel extends Panel
{
public function ResizablePanel()
{
super();
resizer.addEventListener(MouseEvent.MOUSE_DOWN,handleDown)
}
private var resizer:Button=new Button();

override protected function createChildren():void{
this.resizer.width=12;
this.resizer.height=12;

super.createChildren();
this.rawChildren.addChild(resizer);
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth,unscaledHeight);
this.resizer.y = unscaledHeight - resizer.height ;
this.resizer.x = unscaledWidth - resizer.width ;

}
private function handleDown(e:MouseEvent):void{
stage.addEventListener(MouseEvent.MOUSE_MOVE,handleScale)
stage.addEventListener(MouseEvent.MOUSE_UP,stopResize)
}
private function handleScale(e:MouseEvent):void{
if(parent.mouseX-this.x>50)
width=parent.mouseX-this.x;
if(parent.mouseY-this.y>50)
height=parent.mouseY-this.y;
}
private function stopResize(e:MouseEvent):void{
stage.removeEventListener(MouseEvent.MOUSE_MOVE,handleScale)
stage.removeEventListener(MouseEvent.MOUSE_UP,stopResize)
}
}

}