Author Archive for admin

10
Mar

Test Driven Development (TDD) with FlexUnit 4 - Complete Tutorial

After completing my preso at 360|Flex San Jose I realized how many people are having problems starting with Test Driven Development (TDD). I had many people walking up to me and telling me about their interest of starting using TDD and how it’s hard to get started. I decided to share my knowledge. This blog entry includes the following:

  • Complete Tutorial building a “real life” application
  • My 360 presentation
  • Complete Project

You can download the doc, preso and project files from here:
http://eladelrom-preso.googlecode.com/files/TDD.zip

Let’s get started.

As Flash applications become more dynamic and complex, they become more difficult to maintain and scale, particularly when business requirements change throughout the course of development. These challenges are significant and they are common in all types of development, including mobile, web, and desktop applications.

For software engineers, this problem is neither new nor confined to a specific platform. Java and ASP developers have been challenged with the same issues and have found Test Driven Development (TDD) a useful technique for creating applications that can be easily maintained.

Luckily, the new version of FlexUnit 4 just got closer in similarity to the JUnit (http://www.junit.org/) project and supports many of the features JUnit has, and more! FlexUnit4 combines features from the previous FlexUnit 1.0 features and Fluint (http://code.google.com/p/fluint/).

FlexUnit 4 allows you can create tests in order to achieve reliability, stability and maintainability of your application, however without proper understanding how to create tests and how to shift your coding style you will not be able to fully take advantage of FlexUnit 4.

The purpose of this wiki page is to give you the tools you need to shift your coding style and start using FlexUnit 4 to the fullest.

Test Driven Development quick overview

That’s exactly where TDD (Test Driven Development) comes in, but what’s TDD anyway? In short, the concept is pretty simple. You write your tests before you write your code. It’s that simple and worth repeating: write tests before code!

Test Driven Development is a software development technique in which programmers are writing a failed test that will define the functionality before writing the actual code.

Furthermore, TDD is about realizing an act of hypocrisy. As software developers our job is to be lazy. We automate repetitive tasks.

Yet the balance of the time spent developing code is actually spent testing and debugging our code manually (80% as some studies suggest). Why would you choose to do this repetitive and annoying task when you automate all of the others?

What it means is that we can let humans do the work they do best while letting computers do the work they do best and ending up with code that is more stable and more maintainable.

The concept of TDD is based on Extreme Programming (XP) development paradigm, which talks about teams that work on the development of dynamic projects with changing requirements and a development cycle that includes TDD for writing the test before the code itself. TDD is not the complete development cycle; it is only part of the Extreme Programming (XP) development paradigm. Preparing the tests before writing the code helps a development team to demonstrate their work in small steps, rather than making the customer or other stakeholders wait for the complete result.

Moving in small increments also makes it easier to accommodate changing requirements and helps ensure that your code does what it needs to do, and nothing more. It is important to mention that the focus of the TDD technique is to produce code and not to create a testing platform. The ability to test is an added benefit.

TDD is based on the idea that anything you build should be tested and if you are unable to test it, you should think twice about whether you really want to build it.

By now, there are many resources that explain how to create simple application, in order to implement TDD, however I yet to find a tutorial that gives a real application so I decided to use a real life example so you can better understand FlexUnit and TDD. While trying to come up with an example I realized that one of my tasks these days is to create an AIR application similar to Adobe MAX Companion AIR Application for a conference I am organizing called: FlashAndTheCity (http://www.flashandthecity). Creating a real application can show you how does the process goes in real development and not in a fake example that tries to make everything simple and easy, since creating applications is complex and change often even as you build your application.

Defining Application’s Objective

Understanding the application objectives is as important as coding your application. Here’s a lessons we can learn from a Master Carpenter: Measure Twice, Cut Once!

You need to understand what you are developing before you get started. In our case, we are building an application that will do the following:

1. Allow attendees to communicate with each other through twitter API.
2. The class will keep a list of tweets with #FlashAndTheCity hashtag
3. The class will check for updates of tweets often

Take a look at the Adobe MAX Companion AIR application (Figure 1), the application we are building is very similar in functionality.

11
Figure 1: Adobe MAX Companion AIR application

In order to understand the problem we need to be able to explain the problem in an everyday language.

User Stories

A good approach to take to ensure the tasks are defined well is to follow Agile software development mythology. The Agile mythologies talks about creating one or more informal sentences in an everyday or business language, this type of approach is known as a User Story. The User Story should be limited in characters and should fit a small paper note card. The user stories are usually written by the client in order to give direction to build the software. Think of this list as your todo list.

In our case here are the User Stories:

1. Retrieve tweets with #FlashAndTheCity HashTag from Twitter API.
2. Have a service call to twitter and retrieve tweets every few seconds.
3. Login into user’s twitter account and retrieve personal information.
4. Store user’s information so user wouldn’t have to login again every time.
5. Post a tweet on twitter from a user
6. Add #FlashAndTheCity Hashtag to a tweet so user wont have to type it every time and the application will be able to retrieve all tweets related to the conference.
7. Keep a list of tweets with the ability to add a tweet & remove a tweet.

Getting started

Creating the application

With the knowledge of what we need to develop we are ready to get started. The first step is to create the application open Eclipse or Flash Builder 4 Beta and create a new project name Companion (see instructions below).

• Select File > New > Flex Project to create the project.
• For the Project Name, type Companion, ensure you set the project as Desktop application type.
• Click Finish.

See figure 2.
2
Figure 2: Create new Flex project for desktop

Creating the class to be tested

The architecture we will follow is creating a utility helper class that will wrap the Twitter API and provide the ability to access the different methods in Twitter API.

Create a new class and call it TwitterHelper based the class on the EventDispacher super class so the utility class will be able to dispatch events when new tweets are available. (I recommend writing the actual class needed after writing the test, however, I wanted to show the two approaches).

3
Figure 3: Create new ActionScript Class

Once you select Finish, the class is created automatically for you, see code below:


package utils
{
     import flash.events.EventDispatcher;
     import flash.events.IEventDispatcher;

     public class TwitterHelper extends EventDispatcher
     {
          public function TwitterHelper(target:IEventDispatcher=null)
          {
               super(target);
          }
     }
}

What important more than deciding if you want to create this class or not is to ensure that the helper class is not implemented, since we need to follow TDD process and write the test before we write the actual code. Our next step is to create the Test Suite and Test case.

Creating your first Test Suite

The next step is to create a test suite.

A test suite is a composite of tests. It runs a collection of test cases. During development you can create a collection of tests packaged into test suite and once you are done, you can run the test suite to ensure your code is still working correctly after changes have been made.

To create a test suite, choose File > New > Test Suite Class (see Figure 4).

4

Figure 4: Creating a new Test Suite Class in Flash Builder 4

After you select New Test Suite Class a wizard window opens up. Fill in the following information:

• In the New Test Suite Class dialog box, name the class CompanionTestSuite.
• Select New FlexUnit 4 Test (see Figure 5).
• Click Finish.

5

Figure 5: Creating a New Test Suite Class named CompanionTestSuite

Note that Although in Extreme Programming you are encouraged to write tests before creating the code (and ideally that’s how you should work), in real life there are many times where you will find yourself writing the tests after the code. Such decisions are made case by case, and it is OK to adjust the methodology to fit your workflow.

Flash Builder 4 added the following class under the flexUnitTests folder:


package flexUnitTests
{
	[Suite]
	[RunWith("org.flexunit.runners.Suite")]
	public class CompanionTestSuite
	{
	}
}

The Suite metadata tag indicates that the class is a suite. The RunWith tag instructs the test runner to execute the tests that follow it using a specific class. FlexUnit 4 is a collection of runners that will run a complete set of tests. You can define each runner to implement a specific interface. You can, for example, specify a different class to run the tests instead of the default runner built into FlexUnit 4.

Add your first test case class

Next, you need to create a test case. A test case comprises the conditions you want to assert to verify a business requirement or a User Story. Each test case in FlexUnit 4 must be connected to a class. To create the class, follow these steps:

Create the Test Case class:

1. Choose File > New > Test Case Class.
2. Select New FlexUnit 4 Test.
3. Type flexUnitTests as the package.
4. Type TwitterHelperTester as the name.
5. Type utils.TwitterHelper as the Class To Test (see Figure 6).

6

Figure 6: Creating a new Test Case class

In case you are creating tests for a class that already existing methods you can select Next and choose the methods you want to test. In our case we are following TDD and creating the test before writing the code so no need to select any methods and you can hit Finish to complete the process and create the Test Case class.

Important: Ensure that there is a reference in CompanionTestSuite.as to TwitterHelperTester:


package flexUnitTests
{
	[Suite]
	[RunWith("org.flexunit.runners.Suite")]
	public class CompanionTestSuite
	{
		public var twitterHelperTester:TwitterHelperTester;
	}
}

You are ready to start writing test code. Open TwitterHelperTester.as and notice that classToTestRef reference has been created automatically for you. Here is the generated code:


package flexUnitTests
{
	import flexunit.framework.Assert;

	import utils.TwitterHelper;

	public class TwitterHelperTester
	{
		// Reference declaration for class to test
		private var classToTestRef : utils.TwitterHelper;

		public function TwitterHelperTester()
		{
		}
	}
}

Implementing your User Stories

Retrieve Tweets User Story

We can start with the first User Story, Retrieve tweets with #FlashAndTheCity HashTag from Twitter API. See figure 7.

71
Figure 7: Retrieve tweets user story

In case you used FlexUnit 1 you recall that each method you create must start with “test”, to enable the test runner to recognize the method. As a result, the method name was changed to testAdditionMethod. In FlexUnit 4, method names do not need to start with “test”; instead they are recognized by the [test] metadata, so feel free to refactor the method names to names that most suite your need.

The first step in implementing the first user story is to understand what’s your goal here. In our case we are testing that the service is working correctly. We are using a public API that is maintained by Twitter and creating a Mashup application. Using a well maintain API helps us creating our application quickly, however it also store a disadvantage that in any time Twitter may change their API and our application will stop working. We are testing that the fail and success events are dispatching correctly and ensuring that the API is working, see the code below:


package flexUnitTests
{
	import flash.events.Event;

	import flexunit.framework.Assert;

	import org.flexunit.async.Async;

	import utils.TwitterHelper;

	public class TwitterHelperTester
	{
		// Reference declaration for class to test
		private var classToTestRef : utils.TwitterHelper;

		public function TwitterHelperTester()
		{
		}

		[Test(async,timeout="500")]
		public function testRetrieveTweetsBasedOnHashTag():void
		{
                              classToTestRef = new TwitterHelper();
			var EVENT_TYPE:String = "retrieveTweets";

			classToTestRef.addEventListener(EVENT_TYPE, Async.asyncHandler( this,  handleAsyncEvnet, 500 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "http://search.twitter.com/search.json");
		}

		//--------------------------------------------------------------------------
		//
		//  Asynchronous handlers
		//
		//--------------------------------------------------------------------------

		private function handleAsyncEvnet(event:Event, passThroughData:Object):void
		{
			Assert.assertEquals( event.type, "retrieveTweets" );
		}
	}
}

As you can see we are writing a test that the method that calls the twitter API works and it retrieve results. We are referencing a method that doesn’t exist: retrieveTweetsBasedOnHashTag.

FlexUnit 4 incorporated Fluint functionality and it supports enhanced asynchronous and includes asynchronous setup and teardown. This feature is possible by every test including the overhead of the asynchronous script. Notice that we are setting a meta data with a timeout:


[Test(async,timeout="500")]

The test will wait 500 milliseconds for to get retrieveTweets dispatched meaning that the results have been retrieved.

Working on the compilation errors

Save the file and now you see a compile time error:

Call to a possibly undefined method retrieveTweetsBasedOnHashTag through a reference with static type utils:TwitterHelper.

This is actually a good. The compiler is telling you what you need to do next. We can now create the method in TwitterHelper in order to get rid of the compiler error.


package utils
{
     import flash.events.EventDispatcher;
     import flash.events.IEventDispatcher;

     public class TwitterHelper extends EventDispatcher
     {
          public function TwitterHelper(target:IEventDispatcher=null)
          {
               super(target);
          }

		  /**
		   * Method to send a request to retrieve all the tweet with a HashTag
		   * @param hashTag defined hashtag to search
		   *
		   */
		  public function retrieveTweetsBasedOnHashTag(hashTag:String):void
		  {
			  // implement
		  }
     }
}

Notice that I only wrote the minimum code to get rid of the compiler error, I haven’t implemented the method yet.

Compile the project and we don’t have any compile time errors and you can run the tests. Select the Run icon carrot and in the drop menu select FlexUnit Tests.

8

Figure 8: Start FlexUnit tests top menu

Once you selected the Run FlexUnit Tests a wizard gives you the opportunity to select entire classes or individual methods. Select TwitterHelperTester and hit OK, see Figure 9.

9

Figure 9: Select and run FlexUnit Tests window

Review the Results

Flash Builder opens a new window where the tests are run and shows the results of your test, see below:

• 1 total test was run
• 0 were successful
• 1 was a failure
• 0 were errors
• 0 were ignored

10

Figure 10: FlexUnit Test Run results

After the result window shows up you can close it and review the results in Flash Builder

• Close the web browser that was opened by builder
• You should see a new view opened in your Flash Builder called FlexUnit Results
• If you don’t see the window select: Windows > Other Views > Flash Builder > FlexUnit Results will open it
• The Results view will show you similar information to the web browser with much more detail and interactivity
• Note the Red Bar and double click on testSampleMethod in the results view (see Figure 11).
• Flash Builder will take you to that method in your code.
• Note that the failure message in the Failure Trace is the same as the contents of the Assert.fail call.

111

Figure 11: FlexUnit result view in Eclipse

The results view provides several action buttons worth reviewing see Figure 12.
12
Figure 12: results view options

Change the Test from Fail to Pass

In order to pass the test you now need to write the actual code. At this point we wrote the test and once we write the code the test will succeed. I have implemented the code to call Twitter and retrieve results that includes #FlashAndTheCity hashtag, see below:


package utils
{
	import com.adobe.serialization.json.JSON;

	import flash.events.EventDispatcher;

	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;

	public class TwitterHelper extends EventDispatcher
	{
		/**
		 * Holds the service class
		 */
		private var service:HTTPService;

		//--------------------------------------------------------------------------
		//
		//  Default Constructor
		//
		//--------------------------------------------------------------------------
		public function TwitterHelper()
		{
			// implement
		}

	   /**
	    * Method to send a request to retrieve all the tweet with a HashTag
	    * @param hashTag defined hashtag to search
	    * @url	the twitter API url
	    *
	    */
	   public function retrieveTweetsBasedOnHashTag(hashTag:String, url:String):void
	   {
		   service = new HTTPService();
		   service.url = url;
		   service.resultFormat = "text";

		   service.addEventListener(ResultEvent.RESULT, onResults);

		   var object:Object = new Object();
		   object.q = hashTag;
		   service.send( object );
	   }

	   //--------------------------------------------------------------------------
	   //
	   //  Event handlers
	   //
	   //--------------------------------------------------------------------------

	   /**
	    * Method to handle the result of a request to retrieve all the list
	    * @param event
	    *
	    */
	   private function onResults(event:ResultEvent):void
	   {
		   service.removeEventListener(ResultEvent.RESULT, onResults);
		   service.removeEventListener(FaultEvent.FAULT, onFault);

		   var rawData:String = String( event.result );
		   var object:Object = JSON.decode( rawData );
		   var results:Array = object.results as Array;
		   var collection:Vector.<TweetVO> = new Vector.<TweetVO>;

		   results.forEach( function callback(item:*, index:int, array:Array):void {
			   var tweet:TweetVO = new TweetVO( item.from_user, item.from_user_id, item.geo, item.id,
				   item.profile_image_url, item.source, item.text, item.to_user, item.to_user_id );

			   collection.push( tweet );
		   });

		   // dispatch an event that holds the results
		   this.dispatchEvent( new TwitterHelperSuccessEvent( collection ) );
	   }
	}
}

Notice that I am using JSON class, which is part of AS3 CoreLib (http://code.google.com/p/as3corelib/)

Additionally, in order to achieve the test I decided to do few things such as creating a Value Object (VO) to hold the results and create a custom event that will be dispatch once the results are retrieved. The VO holds must of the properties retrieved from Twitter API, see complete code below:


package utils
{
	[Bindable]
	public final class TweetVO
	{
		public var from_user:String;
		public var from_user_id:int;
		public var geo:String;
		public var id:int;
		public var profile_image_url:String;
		public var source:String;
		public var text:String;
		public var to_user:String;
		public var to_user_id:int;		

		public function TweetVO(from_user:String, from_user_id:int, geo:String, id:int, profile_image_url:String,
							source:String, text:String, to_user:String, to_user_id:int)
		{
			this.from_user = from_user;
			this.from_user_id = from_user_id;
			this.geo = geo;
			this.id = id;
			this.profile_image_url = profile_image_url;
			this.source = source;
			this.text = text;
			this.to_user = to_user;
			this.to_user_id = to_user_id;
		}
	}
}

Additionally, I have created a custom event that will hold the constant event type and allow passing the collection of tweets received, see complete code below:


package utils
{
	import flash.events.Event;

	public class TwitterHelperSuccessEvent extends Event
	{
		public static const RETRIEVE_TWEETS:String = "retrieveTweets";

		public var collection:Vector.<TweetVO>;

		public function TwitterHelperSuccessEvent( collection:Vector.<TweetVO> )
		{
			this.collection = collection;
			super( RETRIEVE_TWEETS );
		}
	}
}

Keep it simple: to the sake of simplicity I am keeping all the classes related to our class under the same package but feel free to refactor and place the event under an event folder. Also I haven’t implemented the clone method, which is recommended, but feel free to add these changes.

Run the test again and observe the results, see Figure 13.

• A test that does not fail succeeds
• Click the Run Completed Button
• Observe the Green Bar that indicates success

13
Figure 13: FlexUnit results view showing green bar

One thing to note is that I have set the service to 500 milliseconds, however the request took longer and I the test still failed so I quickly adjusted the code to wait 20000 milliseconds (20 seconds) to accommodate cases where the network is slow, feel free add a test to ensure the respond is quick enough.

To complete the user story we need to also test fault request, so just as we did before let’s write the test and than implement the code.

Add the following test:


[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTagFail():void
		{
                              classToTestRef = new TwitterHelper();
			var EVENT_TYPE:String = "serviceFailure";

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncFaultEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "");
		}

		private function handleAsyncFaultEvnet(event:Event, passThroughData:Object):void
		{
			Assert.assertEquals( event.type, "serviceFailure" );
		}

Run the test and watch the fail results. Refactor TwitterHelper to include the fail method.


 	   public function retrieveTweetsBasedOnHashTag( hashTag:String, url:String ):void
	   {
		   service = new HTTPService();
		   service.url = url;
		   service.resultFormat = "text";

		   service.addEventListener(ResultEvent.RESULT, onResults);
		   service.addEventListener(FaultEvent.FAULT, onFault);

		   var object:Object = new Object();
		   object.q = hashTag;
		   service.send( object );
	   }

	   /**
	    * Holds the fault method in case the service failed
		 *
	    * @param event
	    *
	    */
	   private function onFault(event:FaultEvent):void
	   {
		   service.removeEventListener(ResultEvent.RESULT, onResults);
		   service.removeEventListener(FaultEvent.FAULT, onFault);

		   this.dispatchEvent( new TwitterHelperFailureEvent( event.fault.message ) );
	   }

Make sure to create the custom event for the fail event:


package utils
{
	import flash.events.Event;

	public class TwitterHelperFailureEvent extends Event
	{
		public static const SERVICE_FAILURE:String = "serviceFailure";

		public var message:String;

		public function TwitterHelperFailureEvent( message:String )
		{
			this.message = message;
			super( SERVICE_FAILURE );
		}
	}
}

Test again and you should see a green light, see Figure 14.

14
Figure 14: Result view showing our two test completed successfully

Refactor

At this point, we can do a small refactoring to the test case and include the static method from the custom event instead of having the string attached, which will ensure our tests still pass in case we refactor the event type string, see code below:


		[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTag():void
		{
                              classToTestRef = new TwitterHelper();
			var EVENT_TYPE:String = TwitterHelperSuccessEvent.RETRIEVE_TWEETS;

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "http://search.twitter.com/search.json");
		}

		[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTagFail():void
		{
                              classToTestRef = new TwitterHelper();
			var EVENT_TYPE:String = TwitterHelperFailureEvent.SERVICE_FAILURE;

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncFaultEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "");
		}

Tests have duplication that can become painful to maintain

• In our case, we need to instantiating TwitterHelper in each test.
• Granted, there are just two, but this will grow
• We can solve this problem by using additional metadata provided by FlexUnit 4 called [Before] and [After]
• [Before] can be applied to any method that you wish called before each test.
• [After] will be called after each test

For good measure, let’s add a method name tearMeDown() and mark it with the [After] metadata.

• The [After] method gives you a place to clean up from your test case.
• In this case just set classToTestRef equal to null so the instance is available for garbage collection
• In more complicated tests it is often crucial to remove listeners, etc. in this way. In our case TwitterHelper is already removing the listeners so we don’t need to do that, but many other times you will.


		[Before]
		public function setMeUp():void
		{
			classToTestRef = new TwitterHelper();
		} 

		[After]
		public function tearMeDown():void
		{
			classToTestRef = null;
		}

See the complete test code below:


package flexUnitTests
{
	import flash.events.Event;

	import flexunit.framework.Assert;

	import org.flexunit.async.Async;

	import utils.TwitterHelper;
	import utils.TwitterHelperFailureEvent;
	import utils.TwitterHelperSuccessEvent;

	public class TwitterHelperTester
	{
		// Reference declaration for class to test
		private var classToTestRef : utils.TwitterHelper;

		public function TwitterHelperTester()
		{
		}

		[Before]
		public function setMeUp():void
		{
			classToTestRef = new TwitterHelper();
		} 

		[After]
		public function tearMeDown():void
		{
			classToTestRef = null;
		}		

		[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTag():void
		{
			var EVENT_TYPE:String = TwitterHelperSuccessEvent.RETRIEVE_TWEETS;

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "http://search.twitter.com/search.json");
		}

		[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTagFail():void
		{
			var EVENT_TYPE:String = TwitterHelperFailureEvent.SERVICE_FAILURE;

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncFaultEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "");
		}		

		//--------------------------------------------------------------------------
		//
		//  Asynchronous handlers
		//
		//--------------------------------------------------------------------------

		private function handleAsyncEvnet(event:Event, passThroughData:Object):void
		{
			Assert.assertEquals( event.type, "retrieveTweets" );
		} 		

		private function handleAsyncFaultEvnet(event:Event, passThroughData:Object):void
		{
			Assert.assertEquals( event.type, "serviceFailure" );
		}
	}
}

In addition to refactor the tests you should also refactor the actual code. We focus on passing the test and didn’t care that much about the code, however you may find out that the code is too complex and can be simplify by implementing a design pattern, or just adding some small modification. For instance, I can add metadata so when you instantiate the class and add event listeners in MXML component you will get the event type available automatically. Add the code below to the TwitterHelper class:


	/**
	 *  Success custom event
	 *
	 *  @eventType utils.TwitterHelperSuccessEvent.RETRIEVE_TWEETS
	 */
	[Event(name="retrieveTweets", type="utils.TwitterHelperSuccessEvent")]

	/**
	 *  Failure custome event
	 *
	 *  @eventType utils.TwitterHelperFailureEvent.SERVICE_FAILURE
	 */
	[Event(name="serviceFailure", type="utils.TwitterHelperFailureEvent")]

You can see the complete code for the TwitterHelper class below:


package utils
{
	import com.adobe.serialization.json.JSON;

	import flash.events.EventDispatcher;

	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;

	/**
	 *  Success custom event
	 *
	 *  @eventType utils.TwitterHelperSuccessEvent.RETRIEVE_TWEETS
	 */
	[Event(name="retrieveTweets", type="utils.TwitterHelperSuccessEvent")]

	/**
	 *  Failure custome event
	 *
	 *  @eventType utils.TwitterHelperFailureEvent.SERVICE_FAILURE
	 */
	[Event(name="serviceFailure", type="utils.TwitterHelperFailureEvent")]	

	public class TwitterHelper extends EventDispatcher
	{
		/**
		 * Holds the service class
		 */
		private var service:HTTPService;

		//--------------------------------------------------------------------------
		//
		//  Default Constructor
		//
		//--------------------------------------------------------------------------
		public function TwitterHelper()
		{
			// implement
		}

	   /**
	    * Method to send a request to retrieve all the tweet with a HashTag
	    * @param hashTag defined hashtag to search
		* @url	the twitter API url
	    *
	    */
	   public function retrieveTweetsBasedOnHashTag( hashTag:String, url:String ):void
	   {
		   service = new HTTPService();
		   service.url = url;
		   service.resultFormat = "text";

		   service.addEventListener(ResultEvent.RESULT, onResults);
		   service.addEventListener(FaultEvent.FAULT, onFault);

		   var object:Object = new Object();
		   object.q = hashTag;
		   service.send( object );
	   }

	   //--------------------------------------------------------------------------
	   //
	   //  Event handlers
	   //
	   //--------------------------------------------------------------------------

	   /**
	    * Method to handle the result of a request to retrieve all the list
	    * @param event
	    *
	    */
	   private function onResults(event:ResultEvent):void
	   {
		   service.removeEventListener(ResultEvent.RESULT, onResults);
		   service.removeEventListener(FaultEvent.FAULT, onFault);

		   var rawData:String = String( event.result );
		   var object:Object = JSON.decode( rawData );
		   var results:Array = object.results as Array;
		   var collection:Vector.<TweetVO> = new Vector.<TweetVO>;

		   results.forEach( function callback(item:*, index:int, array:Array):void {
			   var tweet:TweetVO = new TweetVO( item.from_user, item.from_user_id, item.geo, item.id,
				   item.profile_image_url, item.source, item.text, item.to_user, item.to_user_id );

			   collection.push( tweet );
		   });

		   // dispatch an event that holds the results
		   this.dispatchEvent( new TwitterHelperSuccessEvent( collection ) );
	   }

	   /**
	    * Holds the fault method in case the service failed
		 *
	    * @param event
	    *
	    */
	   private function onFault(event:FaultEvent):void
	   {
		   service.removeEventListener(ResultEvent.RESULT, onResults);
		   service.removeEventListener(FaultEvent.FAULT, onFault);

		   this.dispatchEvent( new TwitterHelperFailureEvent( event.fault.message ) );
	   }
	}
}

Retrieve tweets every few seconds User Story

The second user story objective is to call the same method we just created every X seconds so we can keep retrieving tags that contain FlashAndTheCity keyword from Twitter, see Figure 15.

151
Figure 15: Retrieve tweets every few seconds User Story

This type of logic doesn’t have to be included in the utility class we created since it’s a specific implementation of the class and I prefer to keep the class generic so it can be re-used.

In the application I am creating I decided to avoid using any micro-architecture framework since the application is simple in nature and I am the only developer working on the application so no need to add complexity.

The architecture I prefer to implement is to separate the API from the implementation and handle this logic in the actual implementation of the class rather than the utility class. To achieve that I will create a design patter that will help me to separate the view and logic. It’s highly recommended to use some sort of presentation model (also known as code behind), so you can easily separate the data and logic from the view, state and transitions (animations).

This type of separation will allow you to better test your code. In my case I decided to implement the Passive Presentation Model, but there are many other ways to achieve the same goal. I am not going to go into too much detail, but feel free to visit the following link to find out more:
http://blogs.adobe.com/paulw/archives/2007/11/presentation_pa_6.html#more

Since we are using a different class for the implementation this user story I would create a new test case for this class. If you recall last time we created the class before the test case, this time I will create the Test Case and once I get compile time error I will add the class.

Create a new Test Case and fill in the information below, see Figure 16.

• Use TweetListPresenterTester as the name
• New FlexUnit 4 test
• Select Finish

16

Figure 16: Create TweetListPresenterTester Test Case Class

Make sure to add a reference of the Test Case to the Test Suite, otherwise it wont run the test.


package flexUnitTests
{
	[Suite]
	[RunWith("org.flexunit.runners.Suite")]
	public class CompanionTestSuite
	{
		public var twitterHelperTester:TwitterHelperTester;
		public var tweetListPresenterTester:TweetListPresenterTester;
	}
}

The unit test will include a reference to the presenter and will test the method that retrieve tweets every few seconds.


package flexUnitTests
{
	import flexunit.framework.Assert;

	import org.flexunit.async.Async;

	import utils.TwitterHelperSuccessEvent;

	public class TweetListPresenterTester
	{
		// Reference declaration for class to test
		private var classToTestRef : presenter.TweetListPresenter;

		public function TweetListPresenterTester()
		{
     classToTestRef = new TweetListPresenter();
		}

		[Test(async,timeout="2000")]
		public function testRetrieveTweetsEveryFewSeconds():void
		{
			classToTestRef.twitterHelper.addEventListener( TwitterHelperSuccessEvent.RETRIEVE_TWEETS, Async.asyncHandler( this, handleAsyncTweetSuccessEvnet, 2000 ), false, 0, true );
			classToTestRef.retrieveTweetsEveryFewSeconds( 4 );
		}

		//--------------------------------------------------------------------------
		//
		//  Asynchronous handlers
		//
		//--------------------------------------------------------------------------

		private function handleAsyncTweetSuccessEvnet(event:TwitterHelperSuccessEvent, passThroughData:Object):void
		{
			Assert.assertTrue( event.collection.length > 0 );
		}
	}
}

After you compile the application you will get the following compile time errors:

1. Type was not found or was not a compile-time constant: TweetListPresenter
2. Call to a possibly undefined method retrieveTweetsEveryFewSeconds through a reference with static type presenter:TweetListPresenter
3. Access of possibly undefined property twitterHelper through a reference with static type presenter:TweetListPresenter.

Once again, these compile time errors are a good thing, so our first task is to solve these errors by creating the TweetListPresenter class, method retrieveTweetsEveryFewSeconds and property twitterHelper. The test will start the method that should create a timer and call the service every four seconds. Once the service is called we should retrieve some data to indicate that the test succeeded.

I have created TweetListPresenter class and added the method and property so the compile time errors will be fixed see code below:


package presenter
{
	import utils.TwitterHelper;

	public class TweetListPresenter
	{
		public var twitterHelper:TwitterHelper;

		public function TweetListPresenter()
		{
			twitterHelper = new TwitterHelper();
		}

		public function retrieveTweetsEveryFewSeconds( seconds:int ):void
		{
			// implement
		}
	}
}

Run the test and you will see that it fails since we haven’t created the logic yet, see Run FlexUnit Tests wizard below.

17

171
Figure 17: Run FlexUnit Tests wizard to test testRetriveTweetsEveryFewSeconds method

18
Close the results view and observe the results, see Figure 18.

Figure 18: Results view shows testRetriveTweetsEveryFewSeconds have failed

As I explained the utility class we created is a wrapper for the Twitter API and doesn’t include any implementation logic, with that said by creating a presenter we can separate the view from the logic and better test the implementation logic.

The approach is to create a passive presenter that controls the view components passively. Create the application entry point and paste the following code, which will add a view to the application and pass that view to the presenter so it can adjust the view properties, such as adding a list of tweets to a datagrid:


<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
					   xmlns:s="library://ns.adobe.com/flex/spark"
					   xmlns:mx="library://ns.adobe.com/flex/halo"
					   xmlns:view="view.*"
					   creationComplete="creationCompleteHandler()">
	<fx:Script>
		<![CDATA[
			import presenter.TweetListPresenter;

			private var tweetListPresenter : TweetListPresenter;

			protected function creationCompleteHandler():void
			{
				tweetListPresenter = new TweetListPresenter( tweetListView );
			}

		]]>
	</fx:Script>

	<view:TweetListView id="tweetListView" />

</s:WindowedApplication>

Notice that we are passing an instance of the view so we can control the view based on the logic in the presenter class. Next adjust the constructor in the presenter class:


		// Corresponding view
		private var _tweetListView : TweetListView;

		public function TweetListPresenter( tweetListView:TweetListView )
		{
			_tweetListView = tweetListView;

			twitterHelper = new TwitterHelper();
		}

Additionally, create an empty view and name it TweetListView.mxml that we will be using to place the view components and sub-components once we are ready:


<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
		 xmlns:s="library://ns.adobe.com/flex/spark"
		 xmlns:mx="library://ns.adobe.com/flex/halo"
		 width="400" height="300">

</s:Group>

At this point we can compile the application and see an empty screen since we didn’t added any view components.

Write code

We are now ready to write the code needed for our test to pass.


package presenter
{
	import flash.events.TimerEvent;
	import flash.utils.Timer;

	import mx.collections.ArrayCollection;

	import utils.TweetVO;
	import utils.TwitterHelper;
	import utils.TwitterHelperFailureEvent;
	import utils.TwitterHelperSuccessEvent;

	import view.TweetListView;

	public class TweetListPresenter
	{
		/**
		 * Corresponding view
		 */
		private var _tweetListView : TweetListView;

		/**
		 *  Twitter helper utility class instance
		 */
		public var twitterHelper:TwitterHelper;

		/**
		 *   Holds a timer so we will be able to update list
		 */
		public var timer:Timer;

		//--------------------------------------------------------------------------
		//
		//  Default Constructor
		//
		//--------------------------------------------------------------------------

		public function TweetListPresenter( tweetListView:TweetListView )
		{
			_tweetListView = tweetListView;

			twitterHelper = new TwitterHelper();
			twitterHelper.addEventListener( TwitterHelperSuccessEvent.RETRIEVE_TWEETS, onRetrieveTweets );
			twitterHelper.addEventListener( TwitterHelperFailureEvent.SERVICE_FAILURE, onFaultRequest );
		}

		/**
		 * Method to go and retireve tweets every defined number of seconds
		 *
		 * @param seconds
		 *
		 */
		public function retrieveTweetsEveryFewSeconds( seconds:int ):void
		{
			timer = new Timer( seconds*1000, 100000 );
			timer.addEventListener(TimerEvent.TIMER, onTimerHandler);

			timer.start();
		}

		//--------------------------------------------------------------------------
		//
		//  Handlers
		//
		//--------------------------------------------------------------------------		

		/**
		 * Method to handle a timer event
		 *
		 * @param event
		 *
		 */
		private function onTimerHandler( event:TimerEvent ):void
		{
			twitterHelper.retrieveTweetsBasedOnHashTag( "FlashAndTheCity", "http://search.twitter.com/search.json" );
		}

		/**
		 * Handler for results
		 *
		 * @param event
		 *
		 */
		private function onRetrieveTweets( event:TwitterHelperSuccessEvent ):void
		{
			var dataProvider:Array = new Array();

			event.collection.forEach( function callback(item:TweetVO, index:int, vector:Vector.<TweetVO>):void {
				dataProvider.push( item );
			} );

			this._tweetListView.dataGrid.dataProvider = dataProvider;
		}

		/**
		 * Handler for fault
		 *
		 * @param event
		 *
		 */
		private function onFaultRequest( event:TwitterHelperFailureEvent ):void
		{

		}
	}
}

The code have a method called retrieveTweetsEveryFewSeconds, which starts a timer and allow us to call twitterHelper.retrieveTweetsBasedOnHashTag every few seconds. Add a DataGrid to the TweetListView.mxml so we can show the results;


<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
		 xmlns:s="library://ns.adobe.com/flex/spark"
		 xmlns:mx="library://ns.adobe.com/flex/halo"
		 width="400" height="300">

	<mx:DataGrid id="dataGrid" width="500" height="400" />

</s:Group>

Run the test and observe the passed test in the result view, see Figure 19:

19

Figure 19: Results View window showing testRetriveTweetsEveryFewSeconds Method passed

At this point you can also run the application and see the DataGrid shows results every four seconds, see Figure 20.
20

Figure 20: Companion application shows twitter results in a DataGrid

Refactor

I have done some additional refactoring to the code and you can download the complete project from here: Companion.fxp

Conclusion

This article covered Test Driven Development in Flex projects. We first defined the project we are creating by defining our user stories and than created the tests using FlexUnit 4. We followed the TDD process without suger coat the process, but showing a real realistic example you can follow and allow you to understand the process of using TDD.

After learning how to create FlexUnit test suites and test cases as well as follow the process, I hope you are inspired to use TDD on your mobile, web, and desktop Flash applications, and write better, more scalable, and more reusable code.

You can download the doc, preso and project files from here:
http://eladelrom-preso.googlecode.com/files/TDD.zip

Cheers :)

20
Feb

10 Most Popular Audio & Video file formats & Codec used by FlashPlayer

Video and audio files that are deployed on platforms with constrain and limitations such as Web and Mobile devices need to be compressed from the original format.

The compression is the usage of a codec (enCOder/DECoder) algorithm. Codecs, as their name implies, are programs that compress and decompress data. (There are audio codecs as well as video codecs). Compression has two purposes: reduced file size to speed up transmission as well as reduces the needed data storage space on the destination device.
Most codecs are lossy, which means some of the data is lost during compression and cannot be recovered during decompression. Lossless codecs preserve all original data, and are therefore a 100% faithful transcription of the original data set when uncompressed. Lossless compression is not well suited to the web or mobile devices.

A codec can either be a physical device or a software based process that, on the encoding side, manages the compression of raw digital audio or video data into files of reduced size, optimizing both download and playback performance. On the decoding side, the process is reversed, with the codec uncompressing the file to produce a high quality facsimile of the original content. Because the object of compression is to reduce the overall file size and streaming bandwidth requirements for a given segment of video, it is typically necessary or desirable to “throw away” some of the data during the compression process, meaning that when the codec reproduces content, it will have incrementally lower production values than the original. Compression, which discards some data in the compression and optimization process, is called lossy data compression.

At this point, we depart from the domain of science, and cross over into craft and art, because to create an acceptable result, compression algorithms must strike a complex balance between the visual quality of video and the volume of data necessary to render it. For purposes of multimedia content, the key measure of codec performance is the bit rate. In the context of transmitting multimedia data over the Internet or mobile carrier connections, bit rate quantifies the number of bits required per increment of playback time in order for the viewer to see smooth, uninterrupted content. For streaming video, this degree of playback quality is also called goodput. Goodput is the effective transmission rate supporting what the user actually sees on their device - in other words, it is the amount of data transferred after deducting things like internet, network and datalink layer protocol overhead; network congestion; and retransmission of data that was corrupted or lost in transit. The ability to empirically measure the performance of various codecs is key, because they have different strengths, and therefore, different applications.

Essentially, codecs are optimization tools, and they are many and diverse, often with thriving application genres based on them. The choice of a particular codec is driven by what rendering or transmission characteristics are the focus of optimization; what codecs a developer can reasonably assume to be present on the target platforms; and what post processing tools the developer has available for converting raw data into a video file format. It’s unsurprising that there is a great deal of competition among the developers of codec technology, because achieving a big advance in compression without a loss of quality would have tremendous commercial value. But, on the other hand, if all codec technologies were secret, there would be crippling fragmentation resulting from dozens of incompatible proprietary file formats for encoded video. This problem is neatly solved by an extensive, widely embraced standards-making process for video encoding.

Video codec designs are precisely specified by the Motion Picture Experts Group (MPEG), an international body, which includes 350 members representing media industries, universities, and research institutions. MPEG is chartered by the International Standards Organization (ISO) and is tasked with publishing standards documents that detail how various codecs work. What‘s interesting about this is that MPEG’s published specifications assume that the compression of video files is asymmetrical. In this sense, asymmetrical means that is it’s far more complex and difficult to compress data than to decompress it. As a standards making group, MPEG is exclusively interested in creating a framework for interoperability among various vendors’ codecs and products. This effectively means that only the decoding process needs to be enshrined in a public standard. The encoding process is not constrained by a published MPEG standard. As long as the compressed video files can be decoded as described in the MPEG spec, innovators are encouraged to design new and better encoders, achieving advances in optimizations, while secure in the knowledge they’ll reap the accompanying economic benefits. As encoder technology moves forward, the deployed decoder technology will continue to work, because the decoder side has no knowledge of encoder implementation and can’t be broken by encoder evolution.

Since there is a great deal at stake, the exact strategies of popular encoder designs are usually not public, but the nature of general recent advances is an open secret. Most codecs have transitioned from logic which compresses video data frame-by-frame to an object based model, where the encoder detects regions of frames that don’t change rapidly and caches those semi static portions. This is a tremendous advantage for bandwidth constrained scenarios like mobile video, because it prevents transmission of redundant data.

Both transmission speed and quality of the video rendering produced by decoding the results of various encoders can differ dramatically from one encoder implementation to another. In addition, there can be significant trade-offs in video codecs’ decoder runtime performance and resource utilization. It’s a subtle point, but an important one: Codec standards enable interoperability, but they do not imply uniformity of performance or quality across mobile devices. This potentially complicates life for content designers and developers, because it is necessary to know what codec is going to play your content back in order to ensure that video files provide acceptable playback performance. On desktop and laptop computers, there are frequently a variety of codecs available, and the presence or absence of a single one is rarely an issue for content developers. In any case, a desktop video app can request the user to download a needed codec if it isn’t already present. Not so with mobile devices.
Let’s go over some of the most popular audio and video formats.

1: FLV Format

FLV is the most popular video format available on the Internet with some of the best websites engaging their viewers with Flash based videos. This Flash Video format is also available Flash Player 6 as well as available on mobile phones from Flash Lite 3 player.

An FLV file encodes synchronized audio and video streams. The audio and video data within FLV files are encoded in the same way as audio and video within SWF files. SWF files published for Flash Player 6, Flash Player can exchange audio, video, and data over RTMP connections with Adobe Flash Media Server as well.

It is estimated that a one-minute video consumes 2 - 3MB of RAM, while a five-minute video consumes an average of 3 - 4MB. Longer videos play without requiring a linear increase in memory. This is true for progressive, streaming, local, and/or remote.

2: F4V And F4P Format

F4V is associated with FLV and many times you will see them attached together as the same format. What is F4V & F4P is simply the Adobe’s wrapper for the H.264 video. The reason there is even a need for a wrapper is to overcome the limitations that the H.264 format doesn’t support features such as alpha channel or cue points. F4V is available from Flash Player 9.0.r115 and higher. The format maintains dimensions and frame rate of source. The format also eliminates black borders. F4P is the protected video format.

FLV and F4V have open specification:
http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf

3: MPEG-4 Format

MPEG-4 Part 14 also known as MP4 is a collection of audio and video encoding. MPEG-4 is considered the standard as many software companies such as Apple and Microsoft support the format. MPEG-4 is a container that allows you to combine audio and video (as well as other streams) into a single file. MPEG-4 video codec and H.264 are the included standards for video coding and compression. H.264 is the evolutionary step and improvement, providing improved capacity in terms of quality and efficiency. The format is available from Flash Player 9 and above.

4: H.264 Format

H.264 is the next-generation video compression technology in the MPEG-4 standard, also known as MPEG-4 Part 10. H.264 delivers excellent video quality across the entire bandwidth spectrum — right from 3G to High Definition Video Players. This format is preferred because it produces an incredible quality of video with the smallest amount of video data. This means you see crisp, clear video in much smaller files, saving you bandwidth and storage costs over previous generations of video codecs. The format is available from Flash Player 9 as well as Flash Lite 3.1.

5: MP3 format

Part of MPEG-1 standard, and also known as MPEG-1 Audio Layer 3. MP3 is a patented digital audio encoding format. The format is a popular audio format and the standard for lossy data compression of digital audio files.

Flash Player 6.0r40 and later are supporting MP3 and in fact audio in Flash Video files usually encoded as MP3. MP3 also supports ID3 metadata container, which allow passing data about the music file.

6: Advanced Audio Coding (AAC)

Supported by Flash Player 9 Update 3 and later Advanced Audio Coding (AAC) was designed to be the successor of the MP3 format. AAC is high-efficiency (HE) and high-fidelity (HiFi), low-bandwidth audio codec and a standardized, lossy compression data and encoding for digital audio. AAC is a higher quality format than MP3 and generally achieves better sound quality than MP3 at similar bit rates. The format is often packaged in a video format container.

7: MOV format

Available from Flash Player 9 Update 3 and up you can play MOV container using MPEG-4 codecs, it mostly interchangeable in a QuickTime-only environment. This is especially true on hardware devices, such as the Sony PSP and various DVD players; on the software side, most DirectShow.

8: 3GP and 3GPP Format

3GP is a simplified version of the MPEG-4 format; it is designed for mobile use. 3GP is based on MPEG-4 and H.263 video, and AAC or AMR audio The format is design to optimize the video content for mobile and specifically built to accommodate low bandwidths and little storage. 3GP format is a popular format within mobile devices and many support the 3GP format. The file extension is either .3gp for GSM-based Phones or .3g2 for CDMA-based Phone.

9: F4A and F4B format

From Flash Player 9 Adobe supports F4A & F4B audio/mp4 audio for Adobe Flash Player. The format is nothing more than MP4 audio file. F4A stands for an audio file while F4B stands for an audio book. The reason the format even exists is bridge and avoid compatibility issues between different platform such as Adobe Flash Player, Quicktime, iPod etc.

10: M4V and M4A

Flash Player 9 Update 3 supports M4V and M4A. While MP4 is the official extension, apple introduced M4V, M4A formats and are the standard file formats for videos and audio for iTune store, iPod and PlayStation portables.

M4V, M4A file formats are identical to MP4 and can be renamed to MP4. M4V stands for video while M4A by audio layer of MP4 movies.

Notice that M4V files contain DRM and the purchasing users info. You can use Requiem (http://undrm.info/remove-DRM-protection/Requiem-freeware-Mac-and-PC-DRM-remover-for-iTunes-files.htm) to remove the DRM.

So why did Apple created the format anyway? The difference in file extension allows to associate the file type with iTune and when you double click the file format you’ll have iTune opens up in case you have iTune installed. M4V are often used for movies, TV episodes, and music videos.

01
Feb

10 recipes on ADC Cookbooks covering AIR 2.0 new APIs

AIR 2.0 beta is available publicly and when I was asked by Adobe’s marketing team, as well as Apress editor to post some recipes for Flex and AIR in the Adobe Developer connection Cookbooks I decided to combine the two. Adobe are looking to have the Cookbooks gain momentum & I decided to lend a hand and released 10 recipes with applications I built with the new AIR 2.0 APIs while I was part of the Beta program. I revised the applications and they are running correctly on the latest built of AIR available for the pre-release users.

Adobe Developer connection Cookboks

These recipes are listed below:

1: SQLite Manager

SQLiteManager does just that and allows you to set the database settings and then access the manager from anywhere in your application. It makes the process simpler and integrates very well with micro architecture frameworks. View recipe.

2: Retrieve user’s Network Information

Create an example of displaying the user’s available networks… View recipe.

3: Retrieving DNS records

Take a look at the app example, which performs DNS and reverse DNS lookup. View recipe.

4: Launching and Interacting with Native Processes

The application below opens up a text file: foobar.txt using the MacOS TextEdit text editor. View recipe.

5: Recipe for using the File promises API

Application below allows dragging of items from a list into the user’s local machine and then the copying of the files. View recipe.

6: Open file with Default Application

An AIR application that you can browse for a supported file, and watch how the registered application opens the file. View recipe.

7: SimpleAudioRecorder - Microphone Access API

The following application lets you record from any microphone connected. While you record the audio you can see a bar that shows the volume of your voice as a graphic. Once you complete recording, you can playback see the a visualization of your recording and save it as a Wave file. View recipe.

8: Mass Storage Device Detection

The following application will display the existing storage devices available as well as add and remove mass storage devices in case you add a new device or remove a device such as a USB key. View recipe.

9: Multi-touch functionality

We first switch between the different options and than we listen to the gesture events which will fire and display the gesture information in the console. View recipe.

10: WebKit with the SquirrelFish Extreme

Using the same HTML component, take a look at the following example which allows you to test some of the new WebKit functionality. View recipe.

Cheers :)

29
Jan

Creating a WebBrowser app using Robotlegs MVCS implementation in less than 2 min!

MVC Frameworks are notorious for requiring developers to spend most of their time working on the framework’s classes instead of the actual code. Since I posted the article about the Ant tasks for RobotLegs I was asked couple of times to show how to use the Ant tasks.

Why Ant tasks? The whole idea of the Ant tasks is to speed up your development and allow people that are not that familiar with RobotLegs to get started quickly on PC or Mac. I decided to create a video showing how I can create an AIR web browser application using RobotLegs with the MVCS implementation and try to do that in less than 2 mins, pretty bold ha?!

The application is going to be a simple web browser, see final screen shot below:

screen-shot-2010-01-29-at-11102-am

See the video tutorial here:

This movie requires Flash Player 9

In case you are having hard time viewing the video use the following URL:
http://www.screencast.com/users/eladnyc/folders/Jing/media/239bd195-4888-4570-bfea-2ddb1362ea6c

Feel free to download the Ant tasks:
http://github.com/EladElrom/robotlegs-utilities-AntGenerator

Download the WebBrowser AIR project from here.

BTW I did end up cheating and do little copy&paste and it took 2:20 seconds… but you get the point :)

27
Jan

Dear Apple, I will be using #FuckTheIpad hashtag till you install #Flash and #AIR

Today Apple announced the release of the iPad. As someone that owns: iMac 29″, Mac AIR, MacBook Pro and the iPhone I couldn’t be more excited to get a hold of the new device. I had some doubts if Apple will install the Flash Player, but honestly I didn’t think that they will dare to release a device that is capable to use the Flash Player with specs that includes 16GB-64GB flash drive ,1GHz processor and 1024-by-768-pixel resolution. It’s one thing not to install Flash Player on the iPhone with claims that it isn’t ready for the device, but another to release a tablet with the Apple A4 without the Flash Player.

apple-ipad-a4-chip-2_270x119

I was extremely disappointed to find out today that the iPad wont support the Flash Plugin nor Adobe AIR. It probably wont support Flash Lite either. I started the #FUCKTheIpad hashtag on twitter as protest with the following entry:

Dear Apple, I will be using #FuckTheIpad hashtag till you install #Flash and #AIR!

screen-shot-2010-01-27-at-64502-pm

The idea is not to be vulgar or rude, that’s not who I am, but to ensure Apple gets the message and understands that the Flash community has had enough with Apple’s refusal to include Flash in their devices. I don’t believe you can get a true web experience without having the Flash Player installed on a device. Additionally, I believe that the iPad is a great device for developers to develop AIR applications, not to mention it would be great to be able to port existing application such as the New York Times Reader.

In an hour the hashtag was used about 50 times. I encourage the Flash community to use the #FuckTheIpad hashtag when talking about the device on twitter, as well as post letters on the FuckTheIpad.com blog so maybe Apple will understand that we (the Flash community) demand they install the Flash Player, otherwise we wont bless or buy their device.

appletabletb120

Microsoft have tried a similar approach of locking other technologies and end up in court with the EU antitrust action as well as losing the support of many in the development community, since then they have learned. Apple please don’t take the same approach!

To show your support please use the #FUCKTheIpad on twitter and visit http://FuckTheIpad.com, we will post some letters soon and skin the blog.

23
Jan

RobotLegs Presentation Model implementation that access entire component events lifecycle

Last night, I was part of the RIARadio recording about RobotLegs. I have been supportive of the framework ever since I was introduced to it by Joel. We had an awesome time talking about Frameworks, OOP and RobotLegs. I raised a question to Shaun Smith and Joel Hooks about the presentation model vs Mediator and I pointed out that I haven’t seen a solution to the problem I presented on RobotLegs forums. See here. Joel challenged me to create my own implementation. I decided to accept the challenge and take a stab at it and see what I can come up with.

Problem

You cannot capture all the lifecycle events of a components ( such as PREINITIALIZE, INIT_PROGRESS, INITIALIZE & CREATION_COMPLETE) when using the mediator in Robotlegs. In addition I haven’t seen any implementation that uses the Presentation Model (PM) so you can do stuff before the component gets created or just drop an Presentation Model implementation you have done outside of the RobotLegs framework.

Under the hood

So why can’t we capture the component life cycle events in the Mediator?

The reason is that RobotLegs uses the contextView which is using the DisplayObjectContainer. The DisplayObjectContainer capture the event once the view is already added to the stage (see in 231 in MediatorMap.as)

The view gets added to the stage and than “onViewAdded” method being called and the mediator gets created. At this point it’s already too late, since most of the events to init the component have already been fired.

Meaning, even if you’ll change the mediatorBase code to the following:


		public function preRegister():void
		{
			if (flexAvailable &amp;amp;amp;amp;&amp;amp;amp;amp; (viewComponent is UIComponentClass) &amp;amp;amp;amp;&amp;amp;amp;amp; !viewComponent['initialized'])
			{
				IEventDispatcher(viewComponent).addEventListener('preinitialize', onCreationComplete, false, 0, true);
			}
			else
			{
				onRegister();
			}
		}

You cannot guarantee it will fire the pre-initialize event, and in fact when I have done some testing I found out that many times the if statement doesn’t get executed and it uses the onRegister directly.

Solution

So the options were either to modify RobotLegs entirely or try to create a more elegant solution that will use RobotLegs architecture while providing the ability to create presentation model in RobotLegs. Since I couldn’t think of a solution other than creating my own mapping system for the presentation model I started modifying RobotLegs framework, however as I was modifying the framework it hit me that there is a more elegant solution using the existing architecture.

Feel free to view and download (right click to ‘view source’) the full example from here:

screen-shot-2010-01-23-at-110559-am

Implementation

Let’s take a look at the example in detail. My entry point, main MXML application, calls the context:ApplicationContext just like any Robotlegs (MVCS) implementation. I have added component that gets attached to the view called mainView


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/halo"
			   xmlns:context="com.elad.application.*"
			   minWidth="1024" minHeight="768"
			   xmlns:view="com.elad.application.view.*">

	<fx:Declarations>
		<context:ApplicationContext contextView="{this}" />
	</fx:Declarations>

	<view:MainView id="mainView" width="100%" height="100%" />

</s:Application>

Using a presentation model will allows me to easy unit test my application better, since I can create test cases just for the logic and if needed test cases for the view. Additionally, I can replace my view easily and create different views for the same logic, which will help big time with Flex 4 new architecture and Flash Catalyst.

So using presentation model such as passive view I can than split my laundry to two piles, view and presenter:

View:
1. State is in the view
2. Transitions
3. View is passive and is not aware of the Presenter

Presenter:
1. Logic is in the Presenter.
2. Presenter observes view events
3. Presenter updates the view data.
4. Presenter ‘knows’ about the components in the view.
5. Presenter holds the data or point to a data class.

In fact, I have used the exact same example I created for Flash&Flex Magazine with very small modifications. Take a look at TweetListPresenter, which holds the responsibilities that I described:


package com.elad.application.view.presenter
{
	import com.elad.application.view.TweetListView;

	import flash.events.TimerEvent;
	import flash.utils.Timer;

	import mx.events.FlexEvent;

	import org.robotlegs.mvcs.Actor;

	import utils.twitter.TwitterHelper;
	import utils.twitter.events.TwitterHelperFailureEvent;
	import utils.twitter.events.TwitterHelperSuccessEvent;
	import utils.twitter.vo.TweetVO;

	public class TweetListPresenter extends Actor
	{
		/**
		 * Corresponding view
		 */
		private var _tweetListView:TweetListView;

		/**
		 * Method to go and retireve tweets every defined number of seconds
		 *
		 * @param seconds
		 *
		 */		

		public function get tweetListView():TweetListView
		{
			return _tweetListView;
		}

		public function set tweetListView(value:TweetListView):void
		{
			_tweetListView = value;
		}		

		/**
		 *  Twitter helper utility class instance
		 */
		public var twitterHelper:TwitterHelper;

		/**
		 *   Holds a timer so we will be able to update list
		 */
		public var timer:Timer;

		//--------------------------------------------------------------------------
		//
		//  Default Constructor
		//
		//--------------------------------------------------------------------------

		// called right after pre-init
		public function TweetListPresenter()
		{
			_tweetListView = new TweetListView();
			_tweetListView.width = 800;
			_tweetListView.height = 400;

			_tweetListView.addEventListener( FlexEvent.PREINITIALIZE, onPreinitialize );
			_tweetListView.addEventListener( FlexEvent.INITIALIZE, onInitialize );
			_tweetListView.addEventListener( FlexEvent.CREATION_COMPLETE, onComplete );

			twitterHelper = new TwitterHelper();
			twitterHelper.addEventListener( TwitterHelperSuccessEvent.RETRIEVE_TWEETS, onRetrieveTweets );
			twitterHelper.addEventListener( TwitterHelperFailureEvent.SERVICE_FAILURE, onFaultRequest );
		}

		public function retrieveTweetsEveryFewSeconds( seconds:int ):void
		{
			timer = new Timer( seconds*1000, 100000 );
			timer.addEventListener( TimerEvent.TIMER, onTimerHandler, false, 0, true );

			timer.start();
		}

		//--------------------------------------------------------------------------
		//
		//  Handlers
		//
		//--------------------------------------------------------------------------		

		private function onPreinitialize(event:FlexEvent):void
		{
			// implememt
			trace("onPreinitialize");
		}

		private function onInitialize(event:FlexEvent):void
		{
			// implememt
			trace("onInitialize");
		}

		private function onComplete(event:FlexEvent):void
		{
			trace("onComplete");
			retrieveTweetsEveryFewSeconds( 2 );
		}

		/**
		 * Method to handle a timer event
		 *
		 * @param event
		 *
		 */
		private function onTimerHandler( event:TimerEvent ):void
		{
			twitterHelper.retrieveTweetsBasedOnHashTag( "FlashAndTheCity" );
		}

		/**
		 * Handler for results
		 *
		 * @param event
		 *
		 */
		private function onRetrieveTweets( event:TwitterHelperSuccessEvent ):void
		{
			var dataProvider:Array = new Array();

			event.collection.forEach( function callback(item:TweetVO, index:int, vector:Vector.<TweetVO>):void {
				dataProvider.push( item );
			} );

			this._tweetListView.dataGrid.dataProvider = dataProvider;
		}

		/**
		 * Handler for fault
		 *
		 * @param event
		 *
		 */
		private function onFaultRequest( event:TwitterHelperFailureEvent ):void
		{
		}
	}
}

Couple of things to notice.

  • Actor - The presenter extends Actor just like any other RobotLegs actor
  • Component’s life cycle events - The default constructor creates the passive view and set the size as well as the component’s life cycle events

The view is passive and just holds the component (in our case a DataGrid):


<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
		 xmlns:s="library://ns.adobe.com/flex/spark"
		 xmlns:mx="library://ns.adobe.com/flex/halo"
		 width="400" height="300">

	<mx:DataGrid id="dataGrid" width="500" height="400" />

</s:Group> 

The next step is to create the mediator for the main view:


	public class MainViewMediator extends Mediator
	{
		[Inject]
		public var mainView:MainView;

		[Inject]
		public var tweetListPresenter:TweetListPresenter;			

		override public function onRegister():void
		{
			mainView.addElement( tweetListPresenter.tweetListView );
		}
	}

Notice that I am using the DI to inject the presenter just as we do to any Actor in RobotLegs:
[Inject] public var tweetListPresenter:TweetListPresenter;

And once the main view is registered I can than add the component to the display:
mainView.addElement( tweetListPresenter.tweetListView );

Lastly, I need to map everything up in the ApplicationContext class:


		override public function startup():void
		{
			//todo: add commands
            commandMap.mapEvent(ContextEvent.STARTUP, StartupCommand, ContextEvent, true);

			// todo: Add Model
            injector.mapSingleton( TweetListPresenter );

			// todo: Add Services

			// todo: Add View
            mediatorMap.mapView(MainView, MainViewMediator);

			// Startup complete
			dispatchEvent( new ContextEvent( ContextEvent.STARTUP ) );
		}

Take a look at the folder structure:

screen-shot-2010-01-23-at-113505-am

Feel free to download the full example from here, and let me know your thoughts.

Cheers :)

19
Jan

Flexunit 4 with Test Driven Development article on FFDMag

I recently published an article on Flash&Flex Magazine about Flexunit 4 with Test Driven Development. You can download it from here:
http://ffdmag.com/magazine/991-flash-and-mobile-apps-iphone-and-ipod-touch

I decided to use a real life example to help reader better understand FlexUnit 4 and TDD. While trying to come up with an example, I realized that one of my tasks these days is to create and AIR application similar to Adobe MAX Companion AIR Application for a conference I am organizing called FlashAndTheCity. Creating a real application can show you how the process goes in real development and not in a fake example that tries to make everything simple and easy. Creating applications is complex and changes often even as you build your application.

Feel free to download from here and feel free to ping me with any feedback:
http://ffdmag.com/magazine/991-flash-and-mobile-apps-iphone-and-ipod-touch

19
Jan

I am very pleased and honored to join the Adobe Community Experts, now called Adobe Community Professional

I am very pleased and honored to join the amazing list of Adobe Community Experts, now called Adobe Community Professional, this year. It is very gratifying to be acknowledged for the work one put into the Flash community and I will be continue my commitment to share my knowledge as well as help the Flash community with my own event (FlashAndTheCity) as well as speaking and helping other Flash events.

So what is the Adobe Community Professional (ACP) program anyway? “The Adobe Community Experts Program is a community based program made up of Adobe customers who share their product expertise with the world-wide Adobe community. The Adobe Community Experts’ mission is to provide high caliber peer-to-peer communication educating and improving the product skills of Adobe customers worldwide.” - http://www.adobe.com/communities/experts/

Thanks Adobe!

19
Dec

Adobe closed major security concerns with Flash Player 10.0.42.34 however these changes may effect the ability to access swfs when setting Security.allowDomain

It appears that Adobe closed some major security concerns with Flash Player 10.0.42.34 see here, however I believe these security concerns have changed the ability to access swf when setting Security.allowDomain.

ASDOC says:

“If two SWF files are served from different domains — for example, http://siteA.com/swfA.swf and http://siteB.com/siteB.swf — then, by default, Flash Player does not allow swfA.swf to script swfB.swf, nor swfB.swf to script swfA.swf. A SWF file gives SWF files from other domains by calling Security.allowDomain(). This is called cross-domain scripting. By calling Security.allowDomain(”siteA.com”), siteB.swf gives siteA.swf permission to script it.” > see here.

In the example below I am creating the example ASDOC is describing. I will create two application which will be hosted on two separate domain names, however I am unable to change properties in the accessed application, see detail below:

Accessed application will holds a label and set the Security.allowDomain to wild card (*) so any application should be able to load this swf and change properties. See below:


<Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/halo"
			   minWidth="1024" minHeight="768" preinitialize="application1_initializeHandler(event)">
	<fx:Script>
		<![CDATA[
			import mx.events.FlexEvent;

			protected function application1_initializeHandler(event:FlexEvent):void
			{
				Security.allowDomain("*");
			}

		]]>
	</fx:Script>

	<Label id="label" text="Hello from accessed application!"  x="8" y="9"/>

</s:Application>

The second application (accessing application) will be hosted on a separate domain and load the accessed swf and change the label property:


<Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/halo"
			   minWidth="1024" minHeight="768"
			   initialize="initializeHandler()">
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.FlexEvent;

			// define variables
			private var loader:Loader;
			private var content:*;

			// load swf
			private function initializeHandler():void
			{
				loader = new Loader();
				loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
				loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadContent_onComplete);
				loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
				loader.load(new URLRequest("http://zeen.com/temp/bin-debug/AccessedApplication.swf"));

				component.addChild(loader);
			}

			// Event Handler

			private function loadContent_onComplete(event:Event):void
			{
				content = event.target.content;

				var onContentApplicationComplete:Function = function(event:Event):void
				{
					// content loaded successfully
				}

				content.addEventListener(FlexEvent.APPLICATION_COMPLETE, onContentApplicationComplete);
			}

			private function ioErrorHandler(event:IOErrorEvent):void
			{
				loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
				Alert.show(event.text);
			}

			private function securityErrorHandler(event:SecurityErrorEvent):void
			{
				loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
				Alert.show(event.text);
			}          

			// methods to access loaded swf

			private function callAccessedApplication():void
			{
				this.content.document.label.text = "label change!";
			}

		]]>
	</fx:Script>

	<mx:UIComponent id="component" width="400" height="82" x="11" y="43" />

	<Button label="Call accessed application"
			  click="callAccessedApplication()" x="84" y="0"/>   

</s:Application>

I also have set the cross-domain policy on the accessed applciation server to allow access:


<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="*"/>
</cross-domain-policy> 

The swf is loaded successfully, but with a warning alert. Additionally, I am unable to change the label properties on the accessed application, see screen shot below:

screen-shot-2009-12-19-at-71327-pm

The application is hosted here:
http://eladelrom.com/bin-debug/AccessingApplication.html

I am hoping to get a clarification: https://bugs.adobe.com/jira/browse/FP-3513

10
Dec

Lightweight Ant script extensions generator for Robotlegs following best practices.

RobotLegs is one of the newest AS3 Micro-Architecture framework out there and to help folks that are starting with Robotlegs, as well as make life easier for current Robotlegs users, I have created Ant script extensions that generate most of the code you need in order to work with RobotLegs following best practices.

robotlegssketchsmall

The Ant tasks will generate the following:

  • Robotlegs Folder structure.
  • Robotlegs libraries and source code
  • Automatic ThunderBolt integration to each user-gesture.
  • Creating VO.
  • Add event command user gesture
  • Add startup command
  • Add main class and Context class
  • Create model class
  • Create service class
  • Create view and mediator combo
  • Move libraries and source code to project lib

You can download the Ant tasks from GitHub:
http://github.com/EladElrom/robotlegs-utilities-AntGenerator

Feel free to add these tasks to your arsenal.. Cheers :)