flex.scoutant.org


Keyword - closure


2008/12/30

SharedObject to persist your Flash app state - A Design canvas sample

You need JavaScript enabled!

Move some figures arround, press Save, Close app, Re-open and notice how figure location is persisted... View source code [1]

Shared Object

Une application Flash a accès au file-system de façon très restreinte, via la technique des SharedObjet. A l'image des Cookies pour les applications web.

Dans le cas de l'application Flash, on dispose d'un espace de stockage, in fine, orienté objet : tout objet (ou grappe d'objet) AS3 peut être sérialisé en AMF3 pour être stocké sous forme de SharedObject. Pour que la dé-sérialisation opère correctement il convient d'enregistrer les classes, typiquement avec le meta-tag RemoteClass.

package model {
	[RemoteClass( alias="model.ImageVO")]
	public class ImageVO {
		public var source:String;
		public var x:Number;
		public var y:Number;
        }

Write

Ensuite, comme le monte la doc [2], il convient d'obtenir un SharedObject pour une nom donné, de lui adjoindre des données, et de réaliser l'écriture avec la méthode flush().

var mySO:SharedObject = SharedObject.getLocal("chessboard");
mySO.data['figure'] = new ImageVO('King.gif');
mySO.flush();

Read

Pour la lecture, c'est tout a fait similaire, sauf que l'on opèrere un down-cast pour promouvoir le typage fort.

var mySO:SharedObject = SharedObject.getLocal("chessboard");
var figure:ImageVO = mySO.data['figure'] as ImageVO;

Serializing UIComponents?

Notre exemple d'échiquier, est un simple Canvas qui contient des Images. L'exemple illustre également le déplacement d'image via Drag & Drop, explicité dans le post [3].

L'approche naïve, consiste dans un premier temps à tenter de sérialiser le Canvas ou les images. Cela ne fonctionnera pas. Un UIComponent comporte des centaines de propriétés et en générale on veut juste stocker quelques propriétés : dans notre exemple le positionnement x et y et la source pour une Image...

D'où la classe ImageVO, vue ci-dessus. C'est cette classe qui sera persistée. Il s'agit alors de prévoir l'instantiation d'une mx.controls.Image à partir de notre ImageVO. Plusieurs approches sont possibles ; on peut prévoir une sous-classe de Image qui ajoute simplement les accesseurs à la ValueObject correspondante :

public class ImageUI extends Image {
		public function get vo() : ImageVO {
			return new ImageVO( source, x, y, width, height);
		}
		public function set vo(ref:ImageVO):void {
			source = (ref!=null ? ref.source : null) ;
			width = ( ref!=null ? ref.width : 0);
			height = ( ref!=null ? ref.height : 0);
			x = ( ref!= null ? ref.x : 0);
			y = ( ref!= null ? ref.y : 0);
		}
       }

Pour bien faire, on aimerait bien ajouter un constructeur d'Image sur la base de son VO correspondant. Mais l'AS3 ne permet pas d'avoir plusieurs constructeurs, on peut en alternative opter pour une méthode statique de fabrication. Par exemple :

public static function newImage( vo:ImageVO, mouseDownCallback:Function=null) : ImageUI {
			var ref:ImageUI = new ImageUI();
			ref.vo = vo;
			return ref;
		}

Muni de cette classe l'instantiation d'un UIComponent à partir du SharedObjet correspondant est très simple. Reste simplement à voir comment se présente la persistance pour une collection d'instance.

Array of data

Comme on a affaire à une vrai sérialisation AMF3, on peut aussi sérialiser directement un tableau d'objets. Par exemple :

private function doSave():void {
            	mySO.data['figures'] = board.getChildren().map( function( item:ImageUI, index:int, arr:Array):ImageVO { 
                     return item.so
                 } );
            	mySO.flush();
            }

2008/11/22

Closures AS3 a la Groovy with Array.forEach() and Array.map() support

Dans nombre de langage de programmation fonctionnel, il est courant de manipuler des collections et d'appliquer des closures aux membres de la collection.

Un exemple avec Groovy [1] , un langage ciblant la JVM Java :

[ 1, 2, 3, 4, 5 ].each { x -> println x*x }
[ 1, 2, 3, 4, 5 ].collect { x -> x*x }

La dernière ligne produit très naturellement la collection :

[1, 4, 9, 16, 25].

En AS3 on a un support équivalent. Quoi qu'un peut moins elliptique.

Array.forEach() [2]

[1, 2, 3, 4, 5].forEach( traceSquare);
private function traceSquare( x:*, index:int, arr:Array):void {
  trace( "" + x*x);
}

Ceci produit vient la même sortie que la ligne groovy avec each{}.

Array.map() [3]

[1, 2, 3, 4, 5].map( square);
private function square( x:*, index:int, arr:Array): * {
  return x*x;
}

Quand le traitement est court comme ci-dessus, on peut aussi opter pour une définition inline, comme suit :

[1, 2, 3, 4, 5].map( function( x:*, index:int, arr:Array):* { return x*x } );
}

Solution de un-marshalling JSON avec closure Array.map()

Nombre de web services offrent un format Json. Le service de feeds de delicious en est un exemple : http://feeds.delicious.com/v2/json/tags/coutant .

{ "flex": 120, "blazeds": 8, . . .}

Il peut être intéressant de convertir cette réponse Json directement en un array d'objets AS3 représentant un tag (avec son nombre d'occurrence comme propriété). Ceci peut être fait simplement :

tagsAsJson.split(",").map( function( item:*, index:int, arr:Array):Tag { return new Tag(item) } );