Using setter injection to provide a view for base mediators in Robotlegs

 

Yesterday, I came across an issue with an app I’m developing with Robotlegs. The app uses what I call “canvases” to represent each section. There’s some similar functionality between the canvases, so I decided to create a base mediator and extend it for the more specific functionality. A problem arose when I needed to access the view in the base mediator, while injecting it as the subclass in the extending mediator. At first, I tried this ghetto solution, since I only needed the view for one handler:

But as time went on, I added more handlers to the base mediator. The more handlers I added, the uglier the code became, with the AS2-reminiscent this['view'] in each function. I remembered reading something about injection types other than property-based, so I ventured out and discovered setter injection. With it, I can inject the subclass view into the subclass mediator, but still access the base view from the base mediator—classy! Here’s the implementation:

Though this works perfectly fine when extending a base mediator once, I already came across an issue—what do you do when your base mediator is extended twice? I suppose you could use a private getter in the in-between subclass, but if anyone has suggestions, feel free to drop them in the comments.

Paging through the Twitter stream

 

Working with an API can be a hassle—especially if you have no control over it. Twitter’s API has its fair share of obstacles, one of which is paging. When you first look at a method in the Twitter API, you will most likely go straight to the page parameter. It’s simple and expected—page 1 is the most recent and the page number increases as you navigate back in time. This seems like the right solution, but it’s not. The page parameter has a major downside.

twitter_paging_0

Since page 1 is the most recent, the entire paging system is relative and forever changing with every new tweet. This means if your user pages back in time, as new tweets arrive in the Twitter database (for his stream), he’ll see the same tweets finding their way into the next page. If the user pages older once and 20 new tweets arrive in Twitter database, he will see the exact same page the next time he pages older (if the count parameter is set to 20).

twitter_paging_1

This issue can be easily dodged by using the max_id parameter. With it, you can set the maximum tweet id number to appear in the stream, excluding all tweets that appear after it. This can be used in paging by setting max_id to one less than the id of the bottom tweet in the stream. There are, however, a few downsides. Since there is no min_id parameter, you need to store all of the previous max_id parameters, so you can find your way back (assuming you run your app off the raw API data and not a local database). Also, I’ve noticed Twitter caps the number of tweets you can retrieve to about 700 or 2 months (I’m not sure if this is a count-based limit or date-based). This is interesting, considering Twitter indicates in the API docs that they limit the number of retrievable to 3,200. They do note this under “pagination limits” using the page and count parameters, but I assumed we would see this same amount for max_id.

Now, another issue with Twitter pagination, and the original reason I decided to write this post, is gaps in the Twitter stream. If your Twitter app caches tweets to a local database, this is for you. Let’s say someone uses your application once, then takes a day off and returns the next day. In that interim, thousands upon thousands of tweets entered the Twitter database, and possibly your user’s stream. The next time this user starts the application, it loads in the latest tweets, but there is still a slew of tweets from the previous day. What do you do?

twitter_paging_2

Use a combination of the max_id and since_id parameters. Start loading older pages using max_id and when the return is empty, you’ve either caught up to the most recent tweet id from the previous session or you reached Twitter’s limit.

A few of Twitter’s newer methods use cursors for paging, where each result includes two numbers that are used to navigate to the newer or older page. I personally haven’t used any of these methods, so it’s unclear to me whether the numbers are relative or absolute—hopefully, the latter. In any case, I look forward to this implementation in some of the older methods like favorites that doesn’t even have since_id or max_id yet.

Where to find the right fontName

 

To preface, I embed fonts using the Embed metatag and fontName parameter with an SWF as the source. If you’re unfamiliar with this method, it looks like this:

1
2
[Embed(source="assets/fonts.swf", fontName="Helvetica")]
protected static const HELVETICA:String;

This past week, I came across the same issue twice. Up until now, I only worked with fonts that used the fontName as it appears in Flash’s properties panel. Sure, this works for fonts with simple names, but when you start getting fancy, things get messy. I needed to embed Alternate Gothic No. 2 and Flash labeled it as “AlternateGothic” with “No2″ as the style. I set the fontName as “AlternateGothic No2″ and was presented with this familiar gem:

fontname_error

I get shivers each time I see it. After a few minutes of failed guess and check, I set out for a solution. I opened FontExplorer X and discovered that the font is labeled as “AlternateGothic-No2″.I gave it a shot and boom went the dynamite. From then on, I thought that would be the correct fontName—the font’s label in Font Explorer X.

fontname_fontexplorer

I was wrong. This week, I needed to embed Helvetica Neue Roman, which is labeled as “12 pt Helvetica* 55 Roman 05472″ in FontExplorer X—freaky, eh? I tried it out and the error returned. At first, I was bummed because I thought I had a foolproof solution. I was close, but no cigar. I opened Font Book to get a second opinion. Apparently, the font’s true name is “12 pt Helvetica* 55 Roman 05472″, which means FontExplorer X removes redundant whitespace, found in our example between “Roman” and “05472.” I tried it out and, sweet sassy molassy, it worked.

fontname_fontbook

From now on, I’ll consult Font Book when embedding a new font.

Correcting the Canon 5D MKII’s awful audio input

 

Following my last post on the DIY steadycam, I decided to improve upon the second downside of shooting video with the 5D—its audio input. The built-in microphone is completely unusable, adding a grinding noise to every video. I tried using an external mic with the 5D’s input port, but the audio was just as bad because the 5D still controls the levels.

My brother, Ben, is pretty big into audio. He has the Zoom H4, a portable recorder that provides true x/y stereo recording. I looked into it and found that Zoom upgraded it with the H4n. I took the plunge, but also purchased a hot shoe adapter with 3/8″ male thread so it could sit atop the 5D.

When picking up a quick release for the steadycam, I also got one for the recorder. Now, everything is set. Video is captured with the 5D, audio recorded with the H4n, and I have a big smile on my face.

h4n_0

h4n_1

For Each loops are fast—don’t believe the hype

 

Growing up a young boy in rural Macungie, I was always reminded how slow For In loops are and to avoid them when possible. The frequency of this scarred me so deep, I never trusted any two-worded For loop, until now. Looking over a co-workers code, I noticed he was using a For Each loop to access XML nodes. At first, I was going to bring up (because of the scarring) how For Each loops are slower, but then I decided to test it first—I’ve been burned before. I was startled to see that For Each loops CRUSH For loops when accessing XML. The For Each loop is almost five times faster than my beloved For loop!

This had me thinking—if a For Each loop is so much faster with XML, what about Objects and Arrays? It turns out they are slightly faster, but the difference is negligible. Even so, the amount of typing you save with a For Each loop is easily enough for me to ditch For loops when possible. Of course, there are situations in which a For Each loop simply isn’t an option, so don’t go thinking it’s a replacement.

Below are the results and the code used in the tests. I started using Grant Skinner’s PerformanceTest class—if you don’t have it, pick it up. Also, you might notice I adopted Adobe’s coding conventions. After years of using my own standards and getting weird looks from other developers, I decided to conform. I’m actually happy with the cleaner look.

[SWF] Application.swf - 393,881 bytes after decompression
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forInLoopObject (10000 iterations)                                      
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forInLoopObject                                              23     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forEachLoopObject (10000 iterations)                                    
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forEachLoopObject                                            11     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forLoopArray (10000 iterations)                                         
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forLoopArray                                                 13     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forInLoopArray (10000 iterations)                                       
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forInLoopArray                                               50     0.01
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forEachLoopArray (10000 iterations)                                     
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forEachLoopArray                                             11     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forLoopXML (10000 iterations)                                           
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forLoopXML                                                  336     0.03
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forInLoopXML (10000 iterations)                                         
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forInLoopXML                                                345     0.03
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forEachLoopXML (10000 iterations)                                       
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forEachLoopXML                                               70     0.01
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package app.test {
	import com.gskinner.utils.PerformanceTest;
 
	import flash.display.Sprite;
	import flash.utils.setTimeout;
 
	public class Test extends Sprite {
		public var tester:PerformanceTest;
		public var xml:XML;
		public var object:Object;
		public var array:Array;
 
		public function Test()
		{
			tester = PerformanceTest.getInstance();
 
			xml = 
				<tdd>
					<user>ben</user>
					<user>chris</user>
					<user>james</user>
					<user>jonnie</user>
					<user>omar</user>
				</tdd>;
 
			object = {ben:"ben",chris:"chris",james:"james",jonnie:"jonnie",omar:"omar"};
 
			array = ["ben", "chris", "james", "jonnie", "omar"];
 
			setTimeout(test, 1000);
		}
 
		public function test():void {
			var iterations:uint = 10000;
 
			tester.testFunction(forInLoopObject, iterations, "forInLoopObject");
			tester.testFunction(forEachLoopObject, iterations, "forEachLoopObject");
			tester.testFunction(forLoopArray, iterations, "forLoopArray");
			tester.testFunction(forInLoopArray, iterations, "forInLoopArray");
			tester.testFunction(forEachLoopArray, iterations, "forEachLoopArray");
			tester.testFunction(forLoopXML, iterations, "forLoopXML");
			tester.testFunction(forInLoopXML, iterations, "forInLoopXML");
			tester.testFunction(forEachLoopXML, iterations, "forEachLoopXML");
		}
 
		public function forInLoopObject ():void {
			var user:String;
			for (var key:String in object) {
				user = object[key];
			}
		}
		public function forEachLoopObject ():void {
			for each (var user:String in object) {
			}
		}
		public function forLoopArray ():void {
			var user:String;
			var m:uint = array.length;
 
			for (var i:uint = 0; i < m; ++i) {
				user = array[i];
			}
		}
		public function forInLoopArray ():void {
			var user:String;
			for (var key:String in array) {
				user = array[key];
			}
		}
		public function forEachLoopArray ():void {
			for each (var user:String in array) {
			}
		}
		public function forLoopXML ():void {
			var user:XML;
			var m:uint = xml.user.length();
 
			for (var i:uint = 0; i < m; ++i) {
				user = xml.user[i];
			}
		}
		public function forInLoopXML ():void {
			var user:XML;
			for (var key:String in xml.user) {
				user = xml.user[key];
			}
		}
		public function forEachLoopXML ():void {
			for each (var user:XML in xml.user) {
			}
		}
	}
}

Old methods die hard

 

A little over a year ago, I wrote a workaround for displaying multiple spaces in HTML TextField instances. The trick is to add &nbsp; to each space. In non-Flash HTML, this would create two spaces for each space, but since the Flash TextField class is unfortunate in so many ways, it ignores the entity, but not completely. Instead of inserting a space, &nbsp; creates an invisible divider, allowing multiple consecutive spaces in an HTML TextField instance.

In that same article, I shared my HTMLFormat.space(n) method that returns n spaces using a for loop. I’ve been using this method for the past few years, not thinking twice about it. Then, last week, I was showing it to a co-worker, saying it was faster than the String.replace() method. I have no idea why I said that, considering I never tested it—I just assumed. Of course, I was embarrassed when it turned out to be five times slower.

I headed to my Test app to find the best approach. Here’s the code I used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package app.test {
	import flash.display.Sprite;
	import flash.utils.getTimer;
 
	import format.HTMLFormat;
 
	public class Test extends Sprite {
		public function Test () {
			__init ();
		}
 
		private function __init ():void {
			var $a:uint, $A:uint;
			var $before:int, $after:int;
			var $string:String;
 
			$A = 10000;
 
			$string = "Hey there  , what is    going on    ?";
 
			$before = getTimer ();
			for ($a = 0; $a < $A; $a++) {
			 	$string.replace (" ", "&nbsp; ");
			}
			$after = getTimer () - $before
			trace ("String.replace (\" \", \"&nbsp; \");", $after);
 
			$before = getTimer ();
			for ($a = 0; $a < $A; $a++) {
			 	$string.replace (/[ ]/ig, "&nbsp; ");
			}
			$after = getTimer () - $before
			trace ("String.replace (/[ ]/ig, \"&nbsp; \");", $after);
 
			$before = getTimer ();
			for ($a = 0; $a < $A; $a++) {
				"Hey there" + HTMLFormat.space (2) + ", what is" + HTMLFormat.space (4) + "going on" + HTMLFormat.space (4) + "?";
			}
			$after = getTimer () - $before
			trace ("HTMLFormat.space (n);", $after);
 
			$before = getTimer ();
			for ($a = 0; $a < $A; $a++) {
				String ("Hey there" + HTMLFormat.space (2) + ", what is" + HTMLFormat.space (4) + "going on" + HTMLFormat.space (4) + "?")
			}
			$after = getTimer () - $before
			trace ("String (HTMLFormat.space (n));", $after);
 
			$before = getTimer ();
			for ($a = 0; $a < $A; $a++) {
				("Hey there" + HTMLFormat.space (2) + ", what is" + HTMLFormat.space (4) + "going on" + HTMLFormat.space (4) + "?") as String
			}
			$after = getTimer () - $before
			trace ("HTMLFormat.space (n) as String;", $after);
 
			$before = getTimer ();
			for ($a = 0; $a < $A; $a++) {
				"Hey there" + "&nbsp; &nbsp; " + ", what is" + "&nbsp; &nbsp; &nbsp; &nbsp; " + "going on" + "&nbsp; &nbsp; &nbsp; &nbsp; " + "?";
			}
			$after = getTimer () - $before
			trace ("\"&nbsp; \"", $after);
 
			$before = getTimer ();
			for ($a = 0; $a < $A; $a++) {
				String ("Hey there" + "&nbsp; &nbsp; " + ", what is" + "&nbsp; &nbsp; &nbsp; &nbsp; " + "going on" + "&nbsp; &nbsp; &nbsp; &nbsp; " + "?");
			}
			$after = getTimer () - $before
			trace ("String (\"&nbsp; \");", $after);
 
			$before = getTimer ();
			for ($a = 0; $a < $A; $a++) {
				("Hey there" + "&nbsp; &nbsp; " + ", what is" + "&nbsp; &nbsp; &nbsp; &nbsp; " + "going on" + "&nbsp; &nbsp; &nbsp; &nbsp; " + "?") as String;
			}
			$after = getTimer () - $before
			trace ("(\"&nbsp; \") as String;", $after);
 
			$before = getTimer ();
			for ($a = 0; $a < $A; $a++) {
				$string.split (" ").join ("&nbsp; ");
			}
			$after = getTimer () - $before
			trace ("String.split (\" \").join (\"&nbsp; \");", $after);
		}
	}
}

And, the startling results:

String.replace (" ", "&nbsp; "); // 15
String.replace (/[ ]/ig, "&nbsp; "); // 142
HTMLFormat.space (n); // 72
String (HTMLFormat.space (n)); // 73
HTMLFormat.space (n) as String; // 75
"&nbsp; " // 15
String ("&nbsp; "); // 15
("&nbsp; ") as String; // 19
String.split (" ").join ("&nbsp; "); // 178

It looks like your best bet is to use the String.replace () method with a String pattern. Though typing out “&nbsp; “ is just as fast, it’s an inconvenience for the developer to type out and dirties up the code. As you might have guessed, I will retire the HTMLFormat.space (n) method and spend a solid hour refactoring some code.

Decimals in Actionscript

 

I recently noticed an issue with my Scroller (scrollbar) class. When resizing a window and the scrollbar along with it, the scrollbar’s position needs to be reevaluated. To do so, it simply sets the scroll value to what it was before, but now with the changed scroll area. With how I had it before, this wasn’t an issue when dealing with a small amount of content, but the larger the content, the more noticeable the issue. For some reason, I wrote it so the get function would return the value based on scrollbar’s position. This seems okay at first, but there’s something in the approach that makes the scrollable content jump a bit when resizing.

I kept an open mind, considering I wrote the class a good three years ago when transitioning from AS2 to AS3. After a number of tests, I realized my code wasn’t directly the cause of the problem—though it was indirectly. I assumed a value is a value. Because of this, I assumed by setting a child’s coordinates to particular numbers, it would retain those precise numbers as its coordinates. Unfortunately, though understandably so, a child’s coordinates are only so accurate. Since we’re dealing with pixels, there’s no reason for a set of coordinates to retain ten decimal places if the screen is only going to recognize the first two. Not only that, it rounds to the closest .05. For instance, setting a child’s x and y coordinates to 34.82721 and 68.27495 respectively results in the position of 34.85 and 68.30.

To get around this, if you truly need a higher level of precision, you can use secondary x and y properties and set it up like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private var __x:Number = 0;
private var __y:Number = 0;
 
public override function get x ():Number {
	return __x;
}
public override function get y ():Number {
	return __y;
}
 
public override function set x ($x:Number):void {
	super.x = __x = $x;
}
public override function set y ($y:Number):void {
	super.y = __y = $y;
}

[note] There are times when this will come in handy, but there are also instances where you wouldn’t want coordinates so precise, so use it cautiously.

iChat screen sharing makes family tech support too easy

 

iChat Screen Sharing

My sister, Lizzy, IM’d me this morning to help her with email configuration woes. Without blinking, I clicked the “Start screen sharing” button and was on her computer. I was able to fix the issue as well as show her exactly what was wrong without saying, “are you at that step yet?” I’m surprised I automatically clicked to share the screen, since I’ve never used it before. I’ve tried to use it before, but for me, at least, it has had a 1% success rate of connecting properly. Maybe that’s one area Snow Leopard touched on. Regardless, next time your family asks for help from their trusty IT dept (you), try screen sharing.

Combining column values in SQLite

 

SQLite has been my life as of late and because of this I’m discovering of a number of tricks. The one I’ll reveal today, once again makes absolutely no sense at all. (Barney shed some light on my previous find, so it wasn’t as astonishing)

Let’s say we have a database of people, and in this database, we separate names into first and last names—this comes in handy for sorting by last name, etc. In our case, we want to retrieve the names into a combined-form, such as name=Jonnie Hallman instead of firstname=Jonnie, lastname=Hallman. This could easily be done, after the fact, in Actionscript, but we want to optimize the process. SQL wizard, James Hall, suggests the following query:

// Correct in SQL // Incorrect in SQLite
SELECT (firstname + ” ” + lastname) AS fullname FROM tablename;

It doesn’t receive an error in SQLite, but it does return a value of zero for that column. In SQL it works though. Here’s the query that works in SQLite:

// Correct in SQLite
SELECT (firstname || ” ” || lastname) AS fullname FROM tablename;

Stunned?—I am. Why would the logical “OR” equivalent be used to combine two strings? If you have the answer to the insanity, please enlighten me. If not, enjoy the tip!

[update] Thanks for the correction on the logical “OR,” not bitwise.

Escaping single quotes in SQLite

 

I’m working on storing all tweets viewed in DestroyTwitter into a local SQLite database to improve performance with filtering and paging. I’ve already been using databases in order for Groups to work, but to avoid characters that might be trouble for the SQLite queries, I would simply run the escape() method to URL-encode the string. Lately, I’ve been on this testing-high, making sure every bit of code I write is the most efficient to my knowledge. After testing escape() vs String.replace(), I realized that the latter is ten times faster. Of course, it makes perfect sense.

My lazy self months ago used escape() without thinking, but now that I know the incredible speed difference, I’ve created an addSlashes() method to use a regular expression to prefix each single quote with a slash (\). This resulted in an error. After a bit of research aka Googling, I came across a mail archive that said that SQLite escapes single quotes a bit differently. Instead of a slash, you must escape it with another single quote. In all honesty, this makes no sense to me, but it works. Here are examples of what works and what doesn’t:

// Wrong – unescaped apostrophe
INSERT INTO tablename (id, name, text) VALUES (1, ‘Jonnie’, ‘That’s what she said’);

// Wrong – incorrectly escaped apostrophe
INSERT INTO tablename (id, name, text) VALUES (1, ‘Jonnie’, ‘That\’s what she said’);

// Correct – unescaped apostrophe
INSERT INTO tablename (id, name, text) VALUES (1, ‘Jonnie’, ‘That’’s what she said’);

Please note that the correct query above uses two separate single quotes, not one double quote.