« November 2006 | Main | January 2007 »

December 31, 2006

Comment Spam

Over the past couple of days my blog has been heavily hit with comment spam. In the last 10 hours I've gotten more than 100 spam message attempts posted to my blog. I moderate all comments so none of those messages saw the light of day but I still have to deal with them. I've never really tuned the comment spam features of MT and I still haven't. Instead I installed a CAPTCHA system. Previously I had required TypeKey in order to leave a comment but that felt a little to draconian. Not everyone has a TypeKey account or wants to create one. Email whitelists are another option but that still requires that I approve or junk email addresses when they are entered.

I'm all about not even having to look at the spam. While this may mean I automatically trash some important comment or email, I think most people are coming to realize that when 90% or more of all email is spam, people will take drastic measures and at some point a message will be lost. As such I'm now running the SCode plugin. The usability of CAPTCHAs is a concern but unless I go back to TypeKey only comments I don't have another good solution at this point. In fact the use of TypeKey on my system just means that your comment will automatically be posted but you still need to enter the security code in order to post.

Tags: links mt spam

December 23, 2006

Is Operator and Type Safe Lists

In some recent coding I was playing with a custom type system and needed to add some type checking to it. What this means is that at some point I need to verify that some object is really the type I think it should be. Normally when you are coding in AS3 and need to check a type you do something like:

public function testType(object:Object):void {
    if (object is String) {
        trace("It is a String");
    } else if (object is Array) {
        trace("It is an Array");
    } else {
        trace("Its type is ?");
    }
}

testType(new String());
testType(new Array());
testType(new Object());
// Output:
// It is a String
// It is an Array
// Its type is ?

Nothing out of the ordinary there. The important thing to examine though is that the right hand side of the is operator is really an expression. Typically you just type in the class you are testing against, since you are likely to cast it on the next line, but why not try something else like:

public function returnClass(type:String):Class {
    if (type == "String") {
        return String;
    }
    if (type == "Array") {
        return Array;
    }
    throw TypeError("Unknown type " + type);
}
public function testType2(object:Object):void {
    if (object is returnClass("String")) {
        trace("It is a String");
    } else if (object is returnClass("Array")) {
        trace("It is an Array");
    } else {
        trace("Its type is ?");
    }
}

testType2(new String());
testType2(new Array());
testType2(new Object());
// Output:
// It is a String
// It is an Array
// Its type is ?

Why would you want to do such a thing? ActionScript 3 doesn't have support for generics, but by using this expression based format of the is operator you can simulate them. You don't get compile time type checking but runtime checking is better than nothing. Let's consider an application where you want to have type safe lists (you can only add items of a certain type to the list). Instead of having to create a separate list wrapper for every type you might need to support, create a standard type safe list class that knows what type each item should be. There are many different approaches to creating such a class, one possibility that uses composition is this:

package com.example {
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.utils.getQualifiedClassName;

    import mx.collections.ArrayCollection;
    import mx.collections.IList;
    import mx.events.CollectionEvent;

    // Please note this code is illustrative and has not been fully tested!
    public class TypeSafeList implements IList {
        private var _itemType:Class;
        private var _arrayCollection:ArrayCollection;
        private var _eventDispatcher:EventDispatcher;

        public function TypeSafeList(itemType:Class) {
            _itemType = itemType;
            _arrayCollection = new ArrayCollection(new Array());
            _eventDispatcher = new EventDispatcher(this);
            _arrayCollection.addEventListener(CollectionEvent.COLLECTION_CHANGE, relayEvent);
        }

        private function relayEvent(event:Event):void {
            dispatchEvent(event);
        }

        private function checkItemType(object:Object):void {
            if ((object != null) && !(object is _itemType)) {
                throw new TypeError("Item is not correct type. Wanted " + _itemType + " got " + getQualifiedClassName(object) + ".");
            }
        }

        public function addItemAt(item:Object, index:int):void {
            checkItemType(item);
            _arrayCollection.addItemAt(item, index);
        }

        public function get length():int {
            return _arrayCollection.length;
        }

        public function toArray():Array {
            return _arrayCollection.toArray();
        }

        public function getItemAt(index:int, prefetch:int = 0.0):Object {
            return _arrayCollection.getItemAt(index, prefetch);
        }

        public function itemUpdated(item:Object, property:Object = null, oldValue:Object = null, newValue:Object = null):void {
            _arrayCollection.itemUpdated(item, property, oldValue, newValue);
        }

        public function removeAll():void {
            _arrayCollection.removeAll();
        }

        public function getItemIndex(item:Object):int {
            return _arrayCollection.getItemIndex(item);
        }

        public function setItemAt(item:Object, index:int):Object {
            checkItemType(item);
            return _arrayCollection.setItemAt(item, index);
        }

        public function removeItemAt(index:int):Object {
            return _arrayCollection.removeItemAt(index);
        }

        public function addItem(item:Object):void {
            checkItemType(item);
            _arrayCollection.addItem(item);
        }

        public function hasEventListener(type:String):Boolean {
            return _eventDispatcher.hasEventListener(type);
        }

        public function willTrigger(type:String):Boolean {
            return _eventDispatcher.willTrigger(type);
        }

        public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0.0, useWeakReference:Boolean = false):void {
            return _eventDispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
        }

        public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void {
            return _eventDispatcher.removeEventListener(type, listener, useCapture);
        }

        public function dispatchEvent(event:Event):Boolean {
            return _eventDispatcher.dispatchEvent(event);
        }
    }
}

Notice how the addItemAt, setItemAt, and addItem methods call checkItemType before passing the call onto the ArrayCollection which is doing all the dirty work. By checking every place that a user can add new information to the IList we can insure that only objects matching the desired type are added. You may wonder why I'm choosing to implement IList instead of just extending ArrayCollection. In this case I don't want the extra baggage that extending ArrayCollection would bring along (support for ICollectionView and the Proxy access methods). If you did want to extend ArrayCollection you would only need to override methods that add data to the object and not create pass through methods for the other interface methods.

Now with this new TypeSafeList what does using it look like? Just like any other IList object, but with the added benefit of runtime type checking. This is some sample code that creates a TypeSafeList that can only have String items added to it:

var list:IList = new TypeSafeList(String);
trace(list.length);
var text:String = "An Item";
list.addItem(text);
trace(list.getItemAt(0));
list.addItemAt("Before An Item", 0);
trace(list.getItemIndex(text));
try {
    list.addItem(12);
} catch (typeError:TypeError) {
    trace(typeError);
}
try {
    list.addItem(new Array());
} catch (typeError:TypeError) {
    trace(typeError);
}

// Output:
// 0
// An Item
// 1
// TypeError: Item is not correct type. Wanted [class String] got int.
// TypeError: Item is not correct type. Wanted [class String] got Array.

The nice thing with the is operator is that you can also check against interfaces. Consider wanting to create a TypeSafeList that can only contain IUIComponent objects. Not only could this list contain classes that subclass UIComponent but also custom classes that implement the IUIComponent interface (not that you would really want to do such a thing). Some sample code that uses an interface with the TypeSafeList class:

var list:IList = new TypeSafeList(IUIComponent);
list.addItem(new Button());
trace(list.length);
list.addItem(new Canvas());
trace(list.length);
try {
    list.addItem("I'm not valid");
} catch (typeError:TypeError) {
    trace(typeError);
}

// Output:
// 1
// 2
// TypeError: Item is not correct type. Wanted [class IUIComponent] got String.

This entry became a little more than what I had originally planned to write about. I originally planned to stop at explaining the use of an expression for the right hand side of the is operator, but somehow ended up with this TypeSafeList class. I hope that you can see the benefit of the simple is operator and maybe someone will find that sample TypeSafeList class useful, please test it first though.

Tags: as3 flex list type

December 22, 2006

Static Initializers in AS3

I couldn't find much official documentation (if you have links please pass them along) about static initializers in ActionScript 3 so I thought I'd put together a little quick reference. A static initializer is run once when a class is first loaded by the AVM. The typical user of such a block is the compiler, which uses it to assign values to static variables of the class. You also can use it to do more complex static object initialization. The example that I needed it for was to create a lookup object based on constants defined in the class. Consider a class that defines some handy constants:

package com.example {
    public class MIME {
        public static const GIF_MIME_TYPE:String = "image/gif";
        public static const JPG_MIME_TYPE:String = "image/jpeg";
        public static const PNG_MIME_TYPE:String = "image/png";
        public static const SWF_MIME_TYPE:String = "application/x-shockwave-flash";
    }
}

If you wanted to create a lookup table to quickly check if a random string matched one of these you can't use the standard object literal initialization syntax. Consider if this code is added to the MIME class:

// Don't use this, it doesn't do what you think, see below
private static const IMAGE_TYPES:Object = {GIF_MIME_TYPE:true,
    JPG_MIME_TYPE:true,
    PNG_MIME_TYPE:true,
    SWF_MIME_TYPE:true};

public static function isImage(mimeType:String):Boolean {
    return (IMAGE_TYPES[mimeType] == true);
}

If you run this test code, you don't get what you want:

trace(MIME.isImage(MIME.GIF_MIME_TYPE));
// Output: false

False? What's going on is that the text before the : in the object literal is treated as a literal string, not as a variable. What ended up happening was the literal string "GIF_MIME_TYPE" was stored in the associative array. This can be demonstrated by this little test:

trace(MIME.isImage("GIF_MIME_TYPE"));
// Output: true

Obviously not what I wanted. This is where the static initializer comes in. I only want to have to setup the lookup table once and avoid having to track if it is setup, so I put the initialization code in a block at the class level.

package com.example {
    public class MIME {
        private static const IMAGE_TYPES:Object = new Object();
        // This block is run once when the class is first accessed
        {
            IMAGE_TYPES[GIF_MIME_TYPE] = true;
            IMAGE_TYPES[JPG_MIME_TYPE] = true;
            IMAGE_TYPES[PNG_MIME_TYPE] = true;
            IMAGE_TYPES[SWF_MIME_TYPE] = true;
        }
    }
}

Yes those lines are just hanging out in the class itself and not within some method. Truth be told the {} are optional. I like to put them in a block to offset the code and make it more readable. With this updated version of IMAGE_TYPES our two tests from above now do the right thing:

trace(MIME.isImage(MIME.GIF_MIME_TYPE));
// Output: true
trace(MIME.isImage("GIF_MIME_TYPE"));
// Output: false

I should point out that the use of const in the static variable declaration is also a preference thing. Even though the IMAGE_TYPES lookup table is defined as a static constant, the contents of the object can still change at runtime. The only thing that's can't change is the object reference that IMAGE_TYPES points at. For example the following code is invalid:

package com.example {
    public class BAD {
        private static const IMAGE_TYPES:Object = new Object();
        {
            // This is a compile time error
            IMAGE_TYPES = new Object();
        }
    }
}

Trying to compile that will give you the error:

1049: Illegal assignment to a variable specified as constant.

The completed MIME class now looks like this:

package com.example {
    public class MIME {
        public static const GIF_MIME_TYPE:String = "image/gif";
        public static const JPG_MIME_TYPE:String = "image/jpeg";
        public static const PNG_MIME_TYPE:String = "image/png";
        public static const SWF_MIME_TYPE:String = "application/x-shockwave-flash";

        private static const IMAGE_TYPES:Object = new Object()
        // This block is run once when the class is first accessed
        {
            IMAGE_TYPES[GIF_MIME_TYPE] = true;
            IMAGE_TYPES[JPG_MIME_TYPE] = true;
            IMAGE_TYPES[PNG_MIME_TYPE] = true;
            IMAGE_TYPES[SWF_MIME_TYPE] = true;
        }

        public static function isImage(mimeType:String):Boolean {
            return (IMAGE_TYPES[mimeType] == true);
        }
    }
}

I get a handy lookup table populated by constants instead of strings, my test function is short and simple instead of some long if then block, and I don't need to worry about checking each time if my lookup table has been initialized. I'm happy.

Tags: as3 flex initializer static

December 18, 2006

AS3 Casting Issues

In most object oriented languages you can cast an object to something more specific in order to be able to call methods particular to the type it was cast as. This frequently comes up when you have a rich class hierarchy and for some reason can't use polymorphism to separate out behavior or more commonly have a factory like method that needs to take in a bunch of different object types but always return the same type. Since AS3 doesn't support method overloading you can't easily separate out the different object types into specialized methods and instead end up with a big if then else block.

My issue with AS3 comes in with the fact that in such as if then else block you can't easily cast primitive objects without getting some funky behavior or without needing to wrap your cast in order to detect errors. I'm sure that there are some techniques out there to avoid this issue entirely, but having now seen it come up more than once I figured I'd jot down my observations on the issue.

AS3 defines a number of top-level or global functions that mirror primitive AS3 objects types. These include String(), Array(), and Date() (currently undocumented) among others. Because these top-level functions exist, they take precedence over a cast. Consider this method.

public function asString(random:Object):String {
    return String(random);
}

public function asCustom(random:Object):Custom {
    return Custom(random);
}

While the two methods look like they would do the same thing, asString is actually calling a function, while asCustom is doing a cast. AS3 provides the as operator as a way to get around this behavior. The problem is that as won't throw casting errors, instead it just silently returns null. Maybe that's fine for what you need, but it just rubs me the wrong way.

I don't want to always have to check for null because in some cases I know that shouldn't be the case, but I also don't want null introduced because of a bug in the calling code. In my mind tracking down a bug from a class cast error is very different then trying to track down a null pointer error especially since the null could percolate further along in the code compared to the class cast error which would happen at the exact spot the cast was attempted.

The other annoying thing about the difference between cast and as is the trick it can play on you in the debugger especially if you are casting something to a String.

public function testCast():void {
    var a:String;
    a = String(null);
    trace(a);
    trace(a is String);
    a = null as String;
    trace(a);
    trace(a is String);
}
// Output:
// null
// true
// null
// false

In both cases the trace of the variable prints out null, but in the first case it is the actual string null, while in the second it is a null value. As I said before my main issue with as is that it doesn't behave like a true cast.

public function testCastError():void {
    var a:Object = new Object();
    var b:String;
    b = String(a);
    trace(b);
    trace(b is String);
    b = a as String;
    trace(b);
    trace(b is String);
    var c:UIComponent;
    c = UIComponent(a);
}
// Output:
// [object Object]
// true
// null
// false
// TypeError: Error #1034: Type Coercion failed: cannot convert Object@ef409c1 to mx.core.UIComponent.

In all three cases above the Object can't be successfully cast to the target type String and UIComponent. In the first case String() doesn't do a cast, instead we get a string representation of the object. In the second case since the types aren't compatibly as silently returns null. In the third we get what I consider the correct behavior which is a class cast error. Since as has this silent behavior you need to check for null both before and after if you want to distinguish between a null value and an incompatible cast.

In general avoid situations where you need to cast primitive objects or cast things at all, but if you do know that casting isn't always casting in AS3.

Tags: as3 casting flex

December 17, 2006

Best Laid Plans

The frequency with which Friday ends with plans that I hope to accomplish over the weekend and the realization Sunday night that few if any of those plans has been completed seems to increase with each week. It isn't that my weekend is filled with activities that prevent me from working on my list, I just seem to default to an almost couch potato state. In some cases other items take up the time I had planned to spend, but in general I think I default to procrastination. I've talked about that in the past, but in this case I think it is all bad procrastination.

Part of my view on it, is that nothing really bad has ever happened to me as a result of procrastination. Or maybe more importantly I've never directly linked procrastination to anything really bad happening to me. It is possible I recharacterized it as apathy, which while related to procrastination in my mind, is different. In either case I continue to procrastinate.

Tags: life

December 15, 2006

Removing Anonymous Event Listeners

One of the features that I really like about AS3 is its support of closures. There are many instances when you want to do something to an argument but don't really need or want a full blown class to encapsulate the logic. Lately I've been trying to apply this approach to event listeners and in general it just works. My problem can up when I wanted to have the event listener run only once or stop listening after some point in time. Since the removeEventListener method requires a reference to the listener that you want to remove and the listener in this case is a closure I need a way to get a reference to it. Thankfully closures provide a way to do that but it requires introducing another variable into the mix. Thankfully ActionScript provides another way to get access to the same information arguments.callee.

Arguments is a variable available in every function that is automatically supplied by the system. It is the older style of getting access to the arguments of a function that takes in an arbitrary number. In this case though the callee also gives you access to yourself. In the case of an anonymous event listener, this is all that is needed to remove it as an event listener. Now for the example:


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="handleCreationComplete();">
    <mx:Script>
        <![CDATA[
            private function handleCreationComplete():void {
                sampleButton.addEventListener(MouseEvent.CLICK, createClickListener(1));
                sampleButton.addEventListener(MouseEvent.CLICK, createClickListener(2));
            }

            private function createClickListener(clickLimit:int):Function {
                var clickCount:int = 0;
                return function(mouseEvent:MouseEvent):void {
                    clickCount++;
                    trace("Click limit", clickLimit, "registering click", clickCount);
                    if (clickCount >= clickLimit) {
                        trace("Click limit", clickLimit, "no longer listening");
                        mouseEvent.target.removeEventListener(MouseEvent.CLICK, arguments.callee);
                    }
                }
            }
        ]]>
    </mx:Script>
    <mx:Button id="sampleButton" label="Go"/>
</mx:Application>

That gives this output:

Click limit 1 registering click 1
Click limit 1 no longer listening
Click limit 2 registering click 1
Click limit 2 registering click 2
Click limit 2 no longer listening

In this example I'm programmatically adding two anonymous event listeners to my button class. They are anonymous in the fact that the function being executed isn't named. Unlike the creationComplete listener that is attached to the Application, you can only pass around the reference to the function, instead of naming it directly. As I mentioned above, since I want to remove the event listener I can't name it directly. Also since I'm creating multiple instances of it, I can't easily pass the reference to the function around. By instead using arguments.calee I avoid the need to name the function and the need to pass the function reference around.

I'll admit that this is not the most compelling example usage of this technique, but I'm sure you can extrapolate from this.

Tags: as3 event flex

December 12, 2006

Architectural Blueprint for Flex Applications (MVCS)

If you didn't catch Joe Berkovitz's talk at MAX 2006 about an architectural blueprint for Flex applications, it is now posted on Adobe's Flex Developer Center. Joe is the Chief Architect of Allurent, where I work. It is a great read and introduces the concept of Model, View, Controller, and Service (MVCS). Best of all it comes with a sample application that embodies all of the principles outlined in the article.

Tags: architecture flex links

December 11, 2006

Matrix Revisited

I recently watched the Matrix trilogy again. It is growing on me. I suspect that the hype around the two movies combined with the fact that they were released so close to each other may have diminished the effect that they had. I still feel the first movie by far has the fewest flaws, but the other two movies aren't as bad as I originally thought. The Wachowski brothers don't do well with intimate love scenes. Action and philosophy are good, but the touching moments really fall flat, at the is true for all of the movies.

Part of the reason I don't think the follow on movies are as well received is the fact that the mind blowing nature of the dual worlds has been revealed. It's like watching The String, The Usual Suspects, or The Game a second time. All great movies, but after you have seen them once, your watching experience changes since there is a critical piece that you now know that you didn't when you watched it the first time. In this case you aren't watching the same movie again, but a different movie that follows on from the same twist.

Since the cat is already out of the bag, your expectation going in is changed. They don't deliver another mind bender, instead they explore more what the dual nature of these two worlds really means. I think because of that exploration, unless you are really interested in the concept, the movies don't offer as much. While I enjoyed Reloaded and Revolutions in the theater the first time I saw them, I didn't have the same positive reaction as I did when I saw the first Matrix.

Having read some books about philosophy and in particular concepts related to the Matrix, watching the trilogy again, I enjoy some of the subtler aspects they explore. It is because of this that I liked the entire trilogy more this time around.

Tags: matrix

December 7, 2006

Developer Tools

I'm sure many other people already know this, but up to this point I've mostly been using my Mac as an end user. dmg or zip files were the most complicated thing I had to deal with. Today I came to want some of the standard development tools (like make). For Mac OS X 10.4 Apple provides all the goodies in a package called Xcode. You can grab it from their Apple Developer Connection site.

Tags: apple macintosh tools

December 1, 2006

Death to TiVo?

With the holiday season is full swing I've been getting catalogs in the mail left and right. Today included one from Circuit City. I have a hard time just throwing a catalog like that away since it has all kinds of technology toys in it. As a was flipping through, I saw the new TiVo Series 3. I don't quite know when it got released but I know it didn't exist at least a year ago when I put my HD system together.

Circuit City is selling them for $799.99. That's even more than the PS3 (ignoring the eBay frenzy). The same catalog has decent computers that cost less. Never the less I consider just what such a purchase would mean. While I sometimes maybe impulsive, I'll at least make sure it fits into some reasonable disposable income level before just slapping down the plastic.

The first thing I did was look at my most recent bill from RCN. There among a bunch of other overpriced features is the monthly line item "RCN HDTV/DVR CONVERTERBOX" for $12.95. That decodes HD and gives me dual tuner HD DVR capabilities all curiosity of the Motorola DCT6412 III unit. Browsing around the TiVo site, the best deal it looks like you can get is the 3-year pre-paid plan. They also have a special going that it looks like you get a year free. Let's call that then $299 for 4 years or about $6.25 a month.

Now I'm not completely up on HD technology and how that plays with cable and the like. The TiVo site says in order to record two programs at once, I'll need two cable cards from RCN. Doing a little digging I can lease cable cards from RCN for $1.50 a month but would no longer be able to get RCN Video On Demand. That bumps the cost up to $9.25 a month in order to support a TiVo Series 3 (part of that "monthly" cost being a $299 lump sum payment).

At this point then I think I can compare some numbers and features (crude list to follow):

  RCN DVR TiVo Series 3
Hardware Cost $0 $799
Monthly Cost $12.95 $9.25 ($299 amortized over 4 years and $2.50 cable card rental fee)
Tuning Capability 2 Channels 2 Channels
HD Support Yes Yes
HD Recording Capacity 12 Hours (Approximate, I can't find a hard number) 32 Hours
Series Recording Support Yes Yes
Internet Programmability No Yes
Multimedia Support No Yes
Advanced Search/Scheduling No Yes

From a raw technical perspective the TiVo does have more capacity, features, and is THX-Certified. Having used a TiVo series 2, the UI is nicer than the one RCN offers and the TiVo remote just feels better. However, from a raw numbers stand point I can't see shelling out $1098 (to get those features) just to save $10.45 a month. Call me crazy but I don't see the value add of TiVo being worth a thousand dollars. Maybe if I watched more TV those better features and UI might matter, I'd be hard pressed to really justify it even if it was the case.

While the price of the TiVo Series 3 may drop, the fact that you are still paying them a monthly fee, I just don't feel that they offer the value add they used to especially with the feature set cable companies are offering, especially when you consider you need to pay the cable company anyway.

Tags: rcn tivo